diff --git a/CHANGES.md b/CHANGES.md index 874f0e5..d51bdb4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,273 @@ +## v2.48.0 + +* Node v0.12.x users must run with --harmony. _This is the last release that + will support v0.12.x_ +* FIXED: (Promise/A+ compliance) When a promise is rejected with a thenable, + the promise adopts the thenable as its rejection reason instead of waiting + for it to settle. The previous (incorrect) behavior was hidden by bugs in + the `promises-aplus-tests` compliance test suite that were fixed in version + `2.1.1`. +* FIXED: the `webdriver.promise.ControlFlow` now has a consistent execution + order for tasks/callbacks scheduled in different turns of the JS event loop. + Refer to the `webdriver.promise` documentation for more details. +* FIXED: do not drop user auth from the WebDriver server URL. +* FIXED: a single `firefox.Binary` instance may be used to configure and + launch multiple FirefoxDriver sessions. + + var binary = new firefox.Binary(); + var options = new firefox.Options().setBinary(binary); + var builder = new Builder().setFirefoxOptions(options); + + var driver1 = builder.build(); + var driver2 = builder.build(); + +* FIXED: zip files created for transfer to a remote WebDriver server are no + longer compressed. If the zip contained a file that was already compressed, + the server would return an "invalid code lengths set" error. +* FIXED: Surfaced the `loopback` option to `remote/SeleniumServer`. When set, + the server will be accessed using the current host's loopback address. + +## v2.47.0 + +### Notice + +This is the last release for `selenium-webdriver` that will support ES5. +Subsequent releases will depend on ES6 features that are enabled by +[default](https://nodejs.org/en/docs/es6/) in Node v4.0.0. Node v0.12.x will +continue to be supported, but will require setting the `--harmony` flag. + +### Change Summary + +* Add support for [Node v4.0.0](https://nodejs.org/en/blog/release/v4.0.0/) + * Updated `ws` dependency from `0.7.1` to `0.8.0` +* Bumped the minimum supported version of Node from `0.10.x` to `0.12.x`. This + is in accordance with the Node support policy established in `v2.45.0`. + +## v2.46.1 + +* Fixed internal module loading on Windows. +* Fixed error message format on timeouts for `until.elementLocated()` + and `until.elementsLocated()`. + +## v2.46.0 + +* Exposed a new logging API via the `webdriver.logging` module. For usage, see + `example/logging.js`. +* Added support for using a proxy server for WebDriver commands. + See `Builder#usingWebDriverProxy()` for more info. +* Removed deprecated functions: + * Capabilities#toJSON() + * UnhandledAlertError#getAlert() + * chrome.createDriver() + * phantomjs.createDriver() + * promise.ControlFlow#annotateError() + * promise.ControlFlow#await() + * promise.ControlFlow#clearHistory() + * promise.ControlFlow#getHistory() +* Removed deprecated enum values: `ErrorCode.NO_MODAL_DIALOG_OPEN` and + `ErrorCode.MODAL_DIALOG_OPENED`. Use `ErrorCode.NO_SUCH_ALERT` and + `ErrorCode.UNEXPECTED_ALERT_OPEN`, respectively. +* FIXED: The `promise.ControlFlow` will maintain state for promise chains + generated in a loop. +* FIXED: Correct serialize target elements used in an action sequence. +* FIXED: `promise.ControlFlow#wait()` now has consistent semantics for an + omitted or 0-timeout: it will wait indefinitely. +* FIXED: `remote.DriverService#start()` will now fail if the child process dies + while waiting for the server to start accepting requests. Previously, start + would continue to poll the server address until the timeout expired. +* FIXED: Skip launching Firefox with the `-silent` flag to preheat the profile. + Starting with Firefox 38, this would cause the browser to crash. This step, + which was first introduced for Selenium's java client back with Firefox 2, + no longer appears to be required. +* FIXED: 8564: `firefox.Driver#quit()` will wait for the Firefox process to + terminate before deleting the temporary webdriver profile. This eliminates a + race condition where Firefox would write profile data during shutdown, + causing the `rm -rf` operation on the profile directory to fail. + +## v2.45.1 + +* FIXED: 8548: Task callbacks are once again dropped if the task was cancelled + due to a previously uncaught error within the frame. +* FIXED: 8496: Extended the `chrome.Options` API to cover all configuration + options (e.g. mobile emulation and performance logging) documented on the + ChromeDriver [project site](https://sites.google.com/a/chromium.org/chromedriver/capabilities). + +## v2.45.0 + +### Important Policy Change + +Starting with the 2.45.0 release, selenium-webdriver will support the last +two stable minor releases for Node. For 2.45.0, this means Selenium will +support Node 0.10.x and 0.12.x. Support for the intermediate, un-stable release +(0.11.x) is "best-effort". This policy will be re-evaluated once Node has a +major version release (i.e. 1.0.0). + +### Change Summary + +* Added native browser support for Internet Explorer, Opera 26+, and Safari +* With the release of [Node 0.12.0](http://blog.nodejs.org/2015/02/06/node-v0-12-0-stable/) + (finally!), the minimum supported version of Node is now `0.10.x`. +* The `promise` module is now [Promises/A+](https://promisesaplus.com/) + compliant. The biggest compliance change is that promise callbacks are now + invoked in a future turn of the JS event loop. For example: + + var promise = require('selenium-webdriver').promise; + console.log('start'); + promise.fulfilled().then(function() { + console.log('middle'); + }); + console.log('end'); + + // Output in selenium-webdriver@2.44.0 + // start + // middle + // end + // + // Output in selenium-webdriver@2.45.0 + // start + // end + // middle + + The `promise.ControlFlow` class has been updated to track the asynchronous + breaks required by Promises/A+, so there are no changes to task execution + order. +* Updated how errors are annotated on failures. When a task fails, the + stacktrace from when that task was scheduled is appended to the rejection + reason with a `From: ` prefix (if it is an Error object). For example: + + var driver = new webdriver.Builder().forBrowser('chrome').build(); + driver.get('http://www.google.com/ncr'); + driver.call(function() { + driver.wait(function() { + return driver.isElementPresent(webdriver.By.id('not-there')); + }, 2000, 'element not found'); + }); + + This code will fail an error like: + + Error: element not found + Wait timed out after 2002ms + at + From: Task: element not found + at + From: Task: WebDriver.call(function) + at + +* Changed the format of strings returned by `promise.ControlFlow#getSchedule`. + This function now accepts a boolean to control whether the returned string + should include the stacktraces for when each task was scheduled. +* Deprecating `promise.ControlFlow#getHistory`, + `promise.ControlFlow#clearHistory`, and `promise.ControlFlow#annotateError`. + These functions were all intended for internal use and are no longer + necessary, so they have been made no-ops. +* `WebDriver.wait()` may now be used to wait for a promise to resolve, with + an optional timeout. Refer to the API documentation for more information. +* Added support for copying files to a remote Selenium via `sendKeys` to test + file uploads. Refer to the API documentation for more information. Sample + usage included in `test/upload_test.js` +* Expanded the interactions API to include touch actions. + See `WebDriver.touchActions()`. +* FIXED: 8380: `firefox.Driver` will delete its temporary profile on `quit`. +* FIXED: 8306: Stack overflow in promise callbacks eliminated. +* FIXED: 8221: Added support for defining custom command mappings. Includes + support for PhantomJS's `executePhantomJS` (requires PhantomJS 1.9.7 or + GhostDriver 1.1.0). +* FIXED: 8128: When the FirefoxDriver marshals an object to the page for + `executeScript`, it defines additional properties (required by the driver's + implementation). These properties will no longer be enumerable and should + be omitted (i.e. they won't show up in JSON.stringify output). +* FIXED: 8094: The control flow will no longer deadlock when a task returns + a promise that depends on the completion of sub-tasks. + +## v2.44.0 + +* Added the `until` module, which defines common explicit wait conditions. + Sample usage: + + var firefox = require('selenium-webdriver/firefox'), + until = require('selenium-webdriver/until'); + + var driver = new firefox.Driver(); + driver.get('http://www.google.com/ncr'); + driver.wait(until.titleIs('Google Search'), 1000); + +* FIXED: 8000: `Builder.forBrowser()` now accepts an empty string since some + WebDriver implementations ignore the value. A value must still be specified, + however, since it is a required field in WebDriver's wire protocol. +* FIXED: 7994: The `stacktrace` module will not modify stack traces if the + initial parse fails (e.g. the user defined `Error.prepareStackTrace`) +* FIXED: 5855: Added a module (`until`) that defines several common conditions + for use with explicit waits. See updated examples for usage. + +## v2.43.5 + +* FIXED: 7905: `Builder.usingServer(url)` once again returns `this` for + chaining. + +## v2.43.2-4 + +* No changes; version bumps while attempting to work around an issue with + publishing to npm (a version string may only be used once). + +## v2.43.1 + +* Fixed an issue with flakiness when setting up the Firefox profile that could + prevent the driver from initializing properly. + +## v2.43.0 + +* Added native support for Firefox - the Java Selenium server is no longer + required. +* Added support for generator functions to `ControlFlow#execute` and + `ControlFlow#wait`. For more information, see documentation on + `webdriver.promise.consume`. Requires harmony support (run with + `node --harmony-generators` in `v0.11.x`). +* Various improvements to the `Builder` API. Notably, the `build()` function + will no longer default to attempting to use a server at + `http://localhost:4444/wd/hub` if it cannot start a browser directly - + you must specify the WebDriver server with `usingServer(url)`. You can + also set the target browser and WebDriver server through a pair of + environment variables. See the documentation on the `Builder` constructor + for more information. +* For consistency with the other language bindings, added browser specific + classes that can be used to start a browser without the builder. + + var webdriver = require('selenium-webdriver') + chrome = require('selenium-webdriver/chrome'); + + // The following are equivalent. + var driver1 = new webdriver.Builder().forBrowser('chrome').build(); + var driver2 = new chrome.Driver(); + +* Promise A+ compliance: a promise may no longer resolve to itself. +* For consistency with other language bindings, deprecated + `UnhandledAlertError#getAlert` and added `#getAlertText`. + `getAlert` will be removed in `2.45.0`. +* FIXED: 7641: Deprecated `ErrorCode.NO_MODAL_DIALOG_OPEN` and + `ErrorCode.MODAL_DIALOG_OPENED` in favor of the new + `ErrorCode.NO_SUCH_ALERT` and `ErrorCode.UNEXPECTED_ALERT_OPEN`, + respectively. +* FIXED: 7563: Mocha integration no longer disables timeouts. Default Mocha + timeouts apply (2000 ms) and may be changed using `this.timeout(ms)`. +* FIXED: 7470: Make it easier to create WebDriver instances in custom flows for + parallel execution. + +## 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. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + 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 [yyyy] [name of copyright owner] + + 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/NOTICE b/NOTICE new file mode 100644 index 0000000..38f6e61 --- /dev/null +++ b/NOTICE @@ -0,0 +1,2 @@ +Copyright 2011-2015 Software Freedom Conservancy +Copyright 2004-2011 Selenium committers diff --git a/README.md b/README.md index c31e4de..a1e64b6 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,100 @@ # browserstack-webdriver +Selenium is a browser automation library. Most often used for testing +web-applications, Selenium may be used for any task that requires automating +interaction with the browser. + ## Installation -Install the latest published version using `npm`: +Install via npm with npm install browserstack-webdriver -In addition to the npm package, you will to download the WebDriver -implementations you wish to utilize. As of 2.34.0, `browserstack-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 browserstack-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 browserstack-webdriver - ## Usage +Below code is a sample test, which opens Google's homepage, searches for ‘browserstack’, and asks for the title of the search results page. var webdriver = require('browserstack-webdriver'); - + + // Input capabilities + var capabilities = { + 'browserName' : 'firefox', + 'browserstack.user' : BROWSERSTACK_USERNAME, + 'browserstack.key' : BROWSERSTACK_KEY + } + var driver = new webdriver.Builder(). - withCapabilities(webdriver.Capabilities.chrome()). - build(); - - driver.get('http://www.google.com'); - driver.findElement(webdriver.By.name('q')).sendKeys('webdriver'); + usingServer('http://hub.browserstack.com/wd/hub'). + withCapabilities(capabilities). + build(); + + driver.get('http://www.google.com/ncr'); + driver.findElement(webdriver.By.name('q')).sendKeys('BrowserStack'); driver.findElement(webdriver.By.name('btnG')).click(); - driver.wait(function() { - return driver.getTitle().then(function(title) { - return title === 'webdriver - Google Search'; - }); - }, 1000); - + + driver.getTitle().then(function(title) { + console.log(title); + }); + driver.quit(); ## Documentation -Full documentation is available on the [Selenium project wiki](http://code.google.com/p/selenium/wiki/WebDriverJs "User guide"). +API documentation is included in the `docs` directory and is also available +online from the [Selenium project][api]. Addition resources include + +- the #selenium channel on freenode IRC +- the [selenium-users@googlegroups.com][users] list +- [SeleniumHQ](http://www.seleniumhq.org/docs/) documentation ## Issues -Please report any issues using the [Selenium issue tracker](https://github.com/browserstack/selenium-webdriver-nodejs/issues). +Please report any issues using the [Selenium issue tracker][issues]. When using +the issue tracker + +- __Do__ include a detailed description of the problem. +- __Do__ include a link to a [gist](http://gist.github.com/) with any + interesting stack traces/logs (you may also attach these directly to the bug + report). +- __Do__ include a [reduced test case][reduction]. Reporting "unable to find + element on the page" is _not_ a valid report - there's nothing for us to + look into. Expect your bug report to be closed if you do not provide enough + information for us to investigate. +- __Do not__ use the issue tracker to submit basic help requests. All help + inquiries should be directed to the [user forum][users] or #selenium IRC + channel. +- __Do not__ post empty "I see this too" or "Any updates?" comments. These + provide no additional information and clutter the log. +- __Do not__ report regressions on closed bugs as they are not actively + monitored for upates (especially bugs that are >6 months old). Please open a + new issue and reference the original bug in your report. ## 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. +Licensed to the Software Freedom Conservancy (SFC) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The SFC licenses this file +to you 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. + +[api]: http://selenium.googlecode.com/git/docs/api/javascript/index.html +[cla]: http://goo.gl/qC50R +[chrome]: http://chromedriver.storage.googleapis.com/index.html +[gh]: https://github.com/SeleniumHQ/selenium/ +[issues]: https://github.com/SeleniumHQ/selenium/issues +[opera]: https://github.com/operasoftware/operachromiumdriver/releases +[phantomjs]: http://phantomjs.org/ +[reduction]: http://www.webkit.org/quality/reduction.html +[release]: http://selenium-release.storage.googleapis.com/index.html +[users]: https://groups.google.com/forum/#!forum/selenium-users diff --git a/_base.js b/_base.js index 6048179..6769c3c 100644 --- a/_base.js +++ b/_base.js @@ -1,17 +1,19 @@ -// Copyright 2012 Selenium committers -// Copyright 2012 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 @@ -74,46 +76,78 @@ var DEPS_FILE_PATH = (function() { })(); - /** - * Synchronously loads a script into the protected Closure context. - * @param {string} src Path to the file to load. + * Maintains a unique context for Closure library-based code. + * @param {boolean=} opt_configureForTesting Whether to configure a fake DOM + * for Closure-testing code that (incorrectly) assumes a DOM is always + * present. + * @constructor */ -function loadScript(src) { - src = path.normalize(src); - var contents = fs.readFileSync(src, 'utf8'); - vm.runInContext(contents, closure, src); +function Context(opt_configureForTesting) { + var closure = this.closure = vm.createContext({ + console: console, + setTimeout: setTimeout, + setInterval: setInterval, + clearTimeout: clearTimeout, + clearInterval: clearInterval, + process: process, + require: require, + Buffer: Buffer, + Error: Error, + TypeError: TypeError, + CLOSURE_BASE_PATH: path.dirname(CLOSURE_BASE_FILE_PATH) + '/', + CLOSURE_IMPORT_SCRIPT: function(src, opt_srcText) { + if (opt_srcText !== undefined) { + // Windows paths use backslashes, which must be properly escaped before + // evaluated with vm.runInContext. + opt_srcText = opt_srcText.replace(/\\/g, '/'); + vm.runInContext(opt_srcText, closure, src); + } else { + loadScript(src); + } + return true; + }, + CLOSURE_NO_DEPS: !isDevMode(), + CLOSURE_UNCOMPILED_DEFINES: {'goog.json.USE_NATIVE_JSON': true}, + goog: {} + }); + closure.window = closure.top = closure; + + if (opt_configureForTesting) { + closure.document = { + body: {}, + createElement: function() { return {}; }, + getElementsByTagName: function() { return []; } + }; + closure.document.body.ownerDocument = closure.document; + } + + loadScript(CLOSURE_BASE_FILE_PATH); + loadScript(DEPS_FILE_PATH); + + // Redefine retrieveAndExecModule_ to load modules. Closure's version + // assumes XMLHttpRequest is defined (and by extension that scripts + // are being loaded from a server). + closure.goog.retrieveAndExecModule_ = function(src) { + var normalizedSrc = path.normalize(src); + var contents = fs.readFileSync(normalizedSrc, 'utf8'); + contents = closure.goog.wrapModule_(src, contents); + vm.runInContext(contents, closure, normalizedSrc); + }; + + /** + * 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); +var context = new Context(); /** @@ -123,8 +157,8 @@ loadScript(DEPS_FILE_PATH); * @throws {Error} If the symbol has not been defined. */ function closureRequire(symbol) { - closure.goog.require(symbol); - return closure.goog.getObjectByName(symbol); + context.closure.goog.require(symbol); + return context.closure.goog.getObjectByName(symbol); } @@ -159,7 +193,8 @@ exports.exportPublicApi = function(symbol) { if (isDevMode()) { - exports.closure = closure; + exports.closure = context.closure; } +exports.Context = Context; exports.isDevMode = isDevMode; exports.require = closureRequire; diff --git a/builder.js b/builder.js index fc2f40b..788e6f4 100644 --- a/builder.js +++ b/builder.js @@ -1,109 +1,492 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'), +// Use base.require to avoid circular references between index and this module. +var Browser = base.require('webdriver.Browser'), + Capabilities = base.require('webdriver.Capabilities'), 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); +var seleniumServer; - default: - return null; +/** + * Starts an instance of the Selenium server if not yet running. + * @param {string} jar Path to the server jar to use. + * @return {!webdriver.promise.Promise} A promise for the server's + * addrss once started. + */ +function startSeleniumServer(jar) { + if (!seleniumServer) { + // Requiring 'chrome' above would create a cycle: + // index -> builder -> chrome -> index + var remote = require('./remote'); + seleniumServer = new remote.SeleniumServer(jar); } + return seleniumServer.start(); } - /** - * Creates new {@link webdriver.WebDriver WebDriver} instances. + * Creates new {@link webdriver.WebDriver WebDriver} instances. The environment + * variables listed below may be used to override a builder's configuration, + * allowing quick runtime changes. + * + * - {@code SELENIUM_BROWSER}: defines the target browser in the form + * {@code browser[:version][:platform]}. + * + * - {@code SELENIUM_REMOTE_URL}: defines the remote URL for all builder + * instances. This environment variable should be set to a fully qualified + * URL for a WebDriver server (e.g. http://localhost:4444/wd/hub). This + * option always takes precedence over {@code SELENIUM_SERVER_JAR}. + * + * - {@code SELENIUM_SERVER_JAR}: defines the path to the + * + * standalone Selenium server jar to use. The server will be started the + * first time a WebDriver instance and be killed when the process exits. + * + * Suppose you had mytest.js that created WebDriver with + * + * var driver = new webdriver.Builder() + * .forBrowser('chrome') + * .build(); + * + * This test could be made to use Firefox on the local machine by running with + * `SELENIUM_BROWSER=firefox node mytest.js`. Rather than change the code to + * target Google Chrome on a remote machine, you can simply set the + * `SELENIUM_BROWSER` and `SELENIUM_REMOTE_URL` environment variables: + * + * SELENIUM_BROWSER=chrome:36:LINUX \ + * SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \ + * node mytest.js + * + * You could also use a local copy of the standalone Selenium server: + * + * SELENIUM_BROWSER=chrome:36:LINUX \ + * SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \ + * node mytest.js + * * @constructor - * @extends {webdriver.AbstractBuilder} */ var Builder = function() { - goog.base(this); + + /** @private {webdriver.promise.ControlFlow} */ + this.flow_ = null; + + /** @private {string} */ + this.url_ = ''; + + /** @private {?string} */ + this.proxy_ = null; + + /** @private {!webdriver.Capabilities} */ + this.capabilities_ = new Capabilities(); + + /** @private {chrome.Options} */ + this.chromeOptions_ = null; + + /** @private {firefox.Options} */ + this.firefoxOptions_ = null; + + /** @private {opera.Options} */ + this.operaOptions_ = null; + + /** @private {ie.Options} */ + this.ieOptions_ = null; + + /** @private {safari.Options} */ + this.safariOptions_ = null; + + /** @private {boolean} */ + this.ignoreEnv_ = false; +}; + + +/** + * Configures this builder to ignore any environment variable overrides and to + * only use the configuration specified through this instance's API. + * + * @return {!Builder} A self reference. + */ +Builder.prototype.disableEnvironmentOverrides = function() { + this.ignoreEnv_ = true; + return this; +}; + + +/** + * Sets the URL of a remote WebDriver server to use. Once a remote URL has been + * specified, the builder direct all new clients to that server. If this method + * is never called, the Builder will attempt to create all clients locally. + * + * As an alternative to this method, you may also set the `SELENIUM_REMOTE_URL` + * environment variable. + * + * @param {string} url The URL of a remote server to use. + * @return {!Builder} A self reference. + */ +Builder.prototype.usingServer = function(url) { + this.url_ = url; + return this; +}; + + +/** + * @return {string} The URL of the WebDriver server this instance is configured + * to use. + */ +Builder.prototype.getServerUrl = function() { + return this.url_; +}; + + +/** + * Sets the URL of the proxy to use for the WebDriver's HTTP connections. + * If this method is never called, the Builder will create a connection without + * a proxy. + * + * @param {string} proxy The URL of a proxy to use. + * @return {!Builder} A self reference. + */ +Builder.prototype.usingWebDriverProxy = function(proxy) { + this.proxy_ = proxy; + return this; +}; + + +/** + * @return {string} The URL of the proxy server to use for the WebDriver's HTTP + * connections. + */ +Builder.prototype.getWebDriverProxy = function() { + return this.proxy_; +}; + + +/** + * Sets the desired capabilities when requesting a new session. This will + * overwrite any previously set capabilities. + * @param {!(Object|webdriver.Capabilities)} capabilities The desired + * capabilities for a new session. + * @return {!Builder} A self reference. + */ +Builder.prototype.withCapabilities = function(capabilities) { + this.capabilities_ = new Capabilities(capabilities); + return this; +}; + + +/** + * Returns the base set of capabilities this instance is currently configured + * to use. + * @return {!webdriver.Capabilities} The current capabilities for this builder. + */ +Builder.prototype.getCapabilities = function() { + return this.capabilities_; +}; + + +/** + * Configures the target browser for clients created by this instance. + * Any calls to {@link #withCapabilities} after this function will + * overwrite these settings. + * + * You may also define the target browser using the {@code SELENIUM_BROWSER} + * environment variable. If set, this environment variable should be of the + * form `browser[:[version][:platform]]`. + * + * @param {(string|webdriver.Browser)} name The name of the target browser; + * common defaults are available on the {@link webdriver.Browser} enum. + * @param {string=} opt_version A desired version; may be omitted if any + * version should be used. + * @param {string=} opt_platform The desired platform; may be omitted if any + * version may be used. + * @return {!Builder} A self reference. + */ +Builder.prototype.forBrowser = function(name, opt_version, opt_platform) { + this.capabilities_.set(Capability.BROWSER_NAME, name); + this.capabilities_.set(Capability.VERSION, opt_version || null); + this.capabilities_.set(Capability.PLATFORM, opt_platform || null); + return 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. + * @param {!webdriver.ProxyConfig} config The configuration to use. * @return {!Builder} A self reference. */ Builder.prototype.setProxy = function(config) { - this.getCapabilities().set(Capability.PROXY, config); + this.capabilities_.setProxy(config); return this; }; /** - * Sets Chrome-specific options for drivers created by this builder. + * Sets the logging preferences for the created session. Preferences may be + * changed by repeated calls, or by calling {@link #withCapabilities}. + * @param {!(webdriver.logging.Preferences|Object.)} prefs The + * desired logging preferences. + * @return {!Builder} A self reference. + */ +Builder.prototype.setLoggingPrefs = function(prefs) { + this.capabilities_.setLoggingPrefs(prefs); + return this; +}; + + +/** + * Sets whether native events should be used. + * @param {boolean} enabled Whether to enable native events. + * @return {!Builder} A self reference. + */ +Builder.prototype.setEnableNativeEvents = function(enabled) { + this.capabilities_.setEnableNativeEvents(enabled); + return this; +}; + + +/** + * Sets how elements should be scrolled into view for interaction. + * @param {number} behavior The desired scroll behavior: either 0 to align with + * the top of the viewport or 1 to align with the bottom. + * @return {!Builder} A self reference. + */ +Builder.prototype.setScrollBehavior = function(behavior) { + this.capabilities_.setScrollBehavior(behavior); + return this; +}; + + +/** + * Sets the default action to take with an unexpected alert before returning + * an error. + * @param {string} beahvior The desired behavior; should be "accept", "dismiss", + * or "ignore". Defaults to "dismiss". + * @return {!Builder} A self reference. + */ +Builder.prototype.setAlertBehavior = function(behavior) { + this.capabilities_.setAlertBehavior(behavior); + return this; +}; + + +/** + * Sets Chrome specific {@linkplain selenium-webdriver/chrome.Options options} + * for drivers created by this builder. Any logging or proxy settings defined + * on the given options will take precedence over those set through + * {@link #setLoggingPrefs} and {@link #setProxy}, respectively. + * * @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)); + this.chromeOptions_ = options; + return this; }; /** - * @override + * Sets Firefox specific {@linkplain selenium-webdriver/firefox.Options options} + * for drivers created by this builder. Any logging or proxy settings defined + * on the given options will take precedence over those set through + * {@link #setLoggingPrefs} and {@link #setProxy}, respectively. + * + * @param {!firefox.Options} options The FirefoxDriver options to use. + * @return {!Builder} A self reference. + */ +Builder.prototype.setFirefoxOptions = function(options) { + this.firefoxOptions_ = options; + return this; +}; + + +/** + * Sets Opera specific {@linkplain selenium-webdriver/opera.Options options} for + * drivers created by this builder. Any logging or proxy settings defined on the + * given options will take precedence over those set through + * {@link #setLoggingPrefs} and {@link #setProxy}, respectively. + * + * @param {!opera.Options} options The OperaDriver options to use. + * @return {!Builder} A self reference. + */ +Builder.prototype.setOperaOptions = function(options) { + this.operaOptions_ = options; + return this; +}; + + +/** + * Sets Internet Explorer specific + * {@linkplain selenium-webdriver/ie.Options options} for drivers created by + * this builder. Any proxy settings defined on the given options will take + * precedence over those set through {@link #setProxy}. + * + * @param {!ie.Options} options The IEDriver options to use. + * @return {!Builder} A self reference. + */ +Builder.prototype.setIeOptions = function(options) { + this.ieOptions_ = options; + return this; +}; + + +/** + * Sets Safari specific {@linkplain selenium-webdriver/safari.Options options} + * for drivers created by this builder. Any logging settings defined on the + * given options will take precedence over those set through + * {@link #setLoggingPrefs}. + * + * @param {!safari.Options} options The Safari options to use. + * @return {!Builder} A self reference. + */ +Builder.prototype.setSafariOptions = function(options) { + this.safariOptions_ = options; + return this; +}; + + +/** + * Sets the control flow that created drivers should execute actions in. If + * the flow is never set, or is set to {@code null}, it will use the active + * flow at the time {@link #build()} is called. + * @param {webdriver.promise.ControlFlow} flow The control flow to use, or + * {@code null} to + * @return {!Builder} A self reference. + */ +Builder.prototype.setControlFlow = function(flow) { + this.flow_ = flow; + return this; +}; + + +/** + * Creates a new WebDriver client based on this builder's current + * configuration. + * + * @return {!webdriver.WebDriver} A new WebDriver instance. + * @throws {Error} If the current configuration is invalid. */ 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; + // Create a copy for any changes we may need to make based on the current + // environment. + var capabilities = new Capabilities(this.capabilities_); + + var browser; + if (!this.ignoreEnv_ && process.env.SELENIUM_BROWSER) { + browser = process.env.SELENIUM_BROWSER.split(/:/, 3); + capabilities.set(Capability.BROWSER_NAME, browser[0]); + capabilities.set(Capability.VERSION, browser[1] || null); + capabilities.set(Capability.PLATFORM, browser[2] || null); + } + + browser = capabilities.get(Capability.BROWSER_NAME) || capabilities.get(Capability.BROWSER); + + if (typeof browser !== 'string') { + throw TypeError( + 'Target browser must be a string, but is <' + (typeof browser) + '>;' + + ' did you forget to call forBrowser()?'); + } + + if (browser === 'ie') { + browser = Browser.INTERNET_EXPLORER; + } + + // Apply browser specific overrides. + if (browser === Browser.CHROME && this.chromeOptions_) { + capabilities.merge(this.chromeOptions_.toCapabilities()); + + } else if (browser === Browser.FIREFOX && this.firefoxOptions_) { + capabilities.merge(this.firefoxOptions_.toCapabilities()); + + } else if (browser === Browser.INTERNET_EXPLORER && this.ieOptions_) { + capabilities.merge(this.ieOptions_.toCapabilities()); + + } else if (browser === Browser.OPERA && this.operaOptions_) { + capabilities.merge(this.operaOptions_.toCapabilities()); + + } else if (browser === Browser.SAFARI && this.safariOptions_) { + capabilities.merge(this.safariOptions_.toCapabilities()); + } + + // Check for a remote browser. + var url = this.url_; + if (!this.ignoreEnv_) { + if (process.env.SELENIUM_REMOTE_URL) { + url = process.env.SELENIUM_REMOTE_URL; + } else if (process.env.SELENIUM_SERVER_JAR) { + url = startSeleniumServer(process.env.SELENIUM_SERVER_JAR); } + } - // Nope, fall-back to using the default java server. - url = AbstractBuilder.DEFAULT_SERVER_URL; + if (url) { + var executor = executors.createExecutor(url, this.proxy_); + return WebDriver.createSession(executor, capabilities, this.flow_); } - var executor = executors.createExecutor(url); - return WebDriver.createSession(executor, this.getCapabilities()); + // Check for a native browser. + switch (browser) { + case Browser.CHROME: + // Requiring 'chrome' above would create a cycle: + // index -> builder -> chrome -> index + var chrome = require('./chrome'); + return new chrome.Driver(capabilities, null, this.flow_); + + case Browser.FIREFOX: + // Requiring 'firefox' above would create a cycle: + // index -> builder -> firefox -> index + var firefox = require('./firefox'); + return new firefox.Driver(capabilities, this.flow_); + + case Browser.INTERNET_EXPLORER: + // Requiring 'ie' above would create a cycle: + // index -> builder -> ie -> index + var ie = require('./ie'); + return new ie.Driver(capabilities, this.flow_); + + case Browser.OPERA: + // Requiring 'opera' would create a cycle: + // index -> builder -> opera -> index + var opera = require('./opera'); + return new opera.Driver(capabilities, this.flow_); + + case Browser.PHANTOM_JS: + // Requiring 'phantomjs' would create a cycle: + // index -> builder -> phantomjs -> index + var phantomjs = require('./phantomjs'); + return new phantomjs.Driver(capabilities, this.flow_); + + case Browser.SAFARI: + // Requiring 'safari' would create a cycle: + // index -> builder -> safari -> index + var safari = require('./safari'); + return new safari.Driver(capabilities, this.flow_); + + default: + throw new Error('Do not know how to build driver: ' + browser + + '; did you forget to call usingServer(url)?'); + } }; diff --git a/chrome.js b/chrome.js index 6ac6f8e..b00d0c0 100644 --- a/chrome.js +++ b/chrome.js @@ -1,17 +1,116 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Defines a {@linkplain Driver WebDriver} client for the Chrome + * web browser. Before using this module, you must download the latest + * [ChromeDriver release] and ensure it can be found on your system [PATH]. + * + * There are three primary classes exported by this module: + * + * 1. {@linkplain ServiceBuilder}: configures the + * {@link selenium-webdriver/remote.DriverService remote.DriverService} + * that manages the [ChromeDriver] child process. + * + * 2. {@linkplain Options}: defines configuration options for each new Chrome + * session, such as which {@linkplain Options#setProxy proxy} to use, + * what {@linkplain Options#addExtensions extensions} to install, or + * what {@linkplain Options#addArguments command-line switches} to use when + * starting the browser. + * + * 3. {@linkplain Driver}: the WebDriver client; each new instance will control + * a unique browser session with a clean user profile (unless otherwise + * configured through the {@link Options} class). + * + * __Customizing the ChromeDriver Server__ + * + * By default, every Chrome session will use a single driver service, which is + * started the first time a {@link Driver} instance is created and terminated + * when this process exits. The default service will inherit its environment + * from the current process and direct all output to /dev/null. You may obtain + * a handle to this default service using + * {@link #getDefaultService getDefaultService()} and change its configuration + * with {@link #setDefaultService setDefaultService()}. + * + * You may also create a {@link Driver} with its own driver service. This is + * useful if you need to capture the server's log output for a specific session: + * + * var chrome = require('selenium-webdriver/chrome'); + * + * var service = new chrome.ServiceBuilder() + * .loggingTo('/my/log/file.txt') + * .enableVerboseLogging() + * .build(); + * + * var options = new chrome.Options(); + * // configure browser options ... + * + * var driver = new chrome.Driver(options, service); + * + * Users should only instantiate the {@link Driver} class directly when they + * need a custom driver service configuration (as shown above). For normal + * operation, users should start Chrome using the + * {@link selenium-webdriver.Builder}. + * + * __Working with Android__ + * + * The [ChromeDriver][android] supports running tests on the Chrome browser as + * well as [WebView apps][webview] starting in Android 4.4 (KitKat). In order to + * work with Android, you must first start the adb + * + * adb start-server + * + * By default, adb will start on port 5037. You may change this port, but this + * will require configuring a [custom server](#custom-server) that will connect + * to adb on the {@linkplain ServiceBuilder#setAdbPort correct port}: + * + * var service = new chrome.ServiceBuilder() + * .setAdbPort(1234) + * build(); + * // etc. + * + * The ChromeDriver may be configured to launch Chrome on Android using + * {@link Options#androidChrome()}: + * + * var driver = new Builder() + * .forBrowser('chrome') + * .setChromeOptions(new chrome.Options().androidChrome()) + * .build(); + * + * Alternatively, you can configure the ChromeDriver to launch an app with a + * Chrome-WebView by setting the {@linkplain Options#androidActivity + * androidActivity} option: + * + * var driver = new Builder() + * .forBrowser('chrome') + * .setChromeOptions(new chrome.Options() + * .androidPackage('com.example') + * .androidActivity('com.example.Activity')) + * .build(); + * + * [Refer to the ChromeDriver site] for more information on using the + * [ChromeDriver with Android][android]. + * + * [ChromeDriver]: https://sites.google.com/a/chromium.org/chromedriver/ + * [ChromeDriver release]: http://chromedriver.storage.googleapis.com/index.html + * [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29 + * [android]: https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android + * [webview]: https://developer.chrome.com/multidevice/webview/overview + */ 'use strict'; @@ -20,6 +119,7 @@ var fs = require('fs'), var webdriver = require('./index'), executors = require('./executors'), + http = require('./http'), io = require('./io'), portprober = require('./net/portprober'), remote = require('./remote'); @@ -35,8 +135,36 @@ var CHROMEDRIVER_EXE = /** - * Creates {@link remote.DriverService} instances that manage a ChromeDriver - * server. + * Custom command names supported by ChromeDriver. + * @enum {string} + */ +var Command = { + LAUNCH_APP: 'launchApp' +}; + + +/** + * Creates a command executor with support for ChromeDriver's custom commands. + * @param {!webdriver.promise.Promise} url The server's URL. + * @return {!webdriver.CommandExecutor} The new command executor. + */ +function createExecutor(url) { + return new executors.DeferredExecutor(url.then(function(url) { + var client = new http.HttpClient(url); + var executor = new http.Executor(client); + executor.defineCommand( + Command.LAUNCH_APP, + 'POST', '/session/:sessionId/chromium/launch_app'); + return executor; + })); +} + + +/** + * Creates {@link selenium-webdriver/remote.DriverService} instances that manage + * a [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/) + * server in a child process. + * * @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. @@ -65,6 +193,9 @@ var ServiceBuilder = function(opt_exe) { }; +/** @private {string} */ +ServiceBuilder.prototype.path_ = null; + /** @private {number} */ ServiceBuilder.prototype.port_ = 0; @@ -92,6 +223,20 @@ ServiceBuilder.prototype.usingPort = function(port) { }; +/** + * Sets which port adb is listening to. _The ChromeDriver will connect to adb + * if an {@linkplain Options#androidPackage Android session} is requested, but + * adb **must** be started beforehand._ + * + * @param {number} port Which port adb is running on. + * @return {!ServiceBuilder} A self reference. + */ +ServiceBuilder.prototype.setAdbPort = function(port) { + this.args_.push('--adb-port=' + port); + return this; +}; + + /** * Sets the path of the log file the driver should log to. If a log file is * not specified, the driver will log to stderr. @@ -134,6 +279,7 @@ ServiceBuilder.prototype.setNumHttpThreads = function(n) { */ ServiceBuilder.prototype.setUrlBasePath = function(path) { this.args_.push('--url-base=' + path); + this.path_ = path; return this; }; @@ -175,6 +321,8 @@ ServiceBuilder.prototype.build = function() { var args = this.args_.concat(); // Defensive copy. return new remote.DriverService(this.exe_, { + loopback: true, + path: this.path_, port: port, args: webdriver.promise.when(port, function(port) { return args.concat('--port=' + port); @@ -228,14 +376,24 @@ var OPTIONS_CAPABILITY_KEY = 'chromeOptions'; /** * Class for managing ChromeDriver specific options. * @constructor + * @extends {webdriver.Serializable} */ var Options = function() { - /** @private {!Array.} */ - this.args_ = []; + webdriver.Serializable.call(this); + + /** @private {!Object} */ + this.options_ = {}; /** @private {!Array.<(string|!Buffer)>} */ this.extensions_ = []; + + /** @private {?webdriver.logging.Preferences} */ + this.logPrefs_ = null; + + /** @private {?webdriver.ProxyConfig} */ + this.proxy_ = null; }; +util.inherits(Options, webdriver.Serializable); /** @@ -254,11 +412,15 @@ Options.fromCapabilities = function(capabilities) { options. addArguments(o.args || []). addExtensions(o.extensions || []). - detachDriver(!!o.detach). + detachDriver(o.detach). + excludeSwitches(o.excludeSwitches || []). setChromeBinaryPath(o.binary). - setChromeLogFile(o.logFile). + setChromeLogFile(o.logPath). + setChromeMinidumpPath(o.minidumpPath). setLocalState(o.localState). - setUserPreferences(o.prefs); + setMobileEmulation(o.mobileEmulation). + setUserPreferences(o.prefs). + setPerfLoggingPrefs(o.perfLoggingPrefs); } if (capabilities.has(webdriver.Capability.PROXY)) { @@ -266,7 +428,7 @@ Options.fromCapabilities = function(capabilities) { } if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) { - options.setLoggingPreferences( + options.setLoggingPrefs( capabilities.get(webdriver.Capability.LOGGING_PREFS)); } @@ -283,7 +445,28 @@ Options.fromCapabilities = function(capabilities) { * @return {!Options} A self reference. */ Options.prototype.addArguments = function(var_args) { - this.args_ = this.args_.concat.apply(this.args_, arguments); + var args = this.options_.args || []; + args = args.concat.apply(args, arguments); + if (args.length) { + this.options_.args = args; + } + return this; +}; + + +/** + * List of Chrome command line switches to exclude that ChromeDriver by default + * passes when starting Chrome. Do not prefix switches with "--". + * + * @param {...(string|!Array)} var_args The switches to exclude. + * @return {!Options} A self reference. + */ +Options.prototype.excludeSwitches = function(var_args) { + var switches = this.options_.excludeSwitches || []; + switches = switches.concat.apply(switches, arguments); + if (switches.length) { + this.options_.excludeSwitches = switches; + } return this; }; @@ -297,8 +480,7 @@ Options.prototype.addArguments = function(var_args) { * @return {!Options} A self reference. */ Options.prototype.addExtensions = function(var_args) { - this.extensions_ = this.extensions_.concat.apply( - this.extensions_, arguments); + this.extensions_ = this.extensions_.concat.apply(this.extensions_, arguments); return this; }; @@ -315,7 +497,7 @@ Options.prototype.addExtensions = function(var_args) { * @return {!Options} A self reference. */ Options.prototype.setChromeBinaryPath = function(path) { - this.binary_ = path; + this.options_.binary = path; return this; }; @@ -329,7 +511,7 @@ Options.prototype.setChromeBinaryPath = function(path) { * @return {!Options} A self reference. */ Options.prototype.detachDriver = function(detach) { - this.detach_ = detach; + this.options_.detach = detach; return this; }; @@ -341,7 +523,7 @@ Options.prototype.detachDriver = function(detach) { * @return {!Options} A self reference. */ Options.prototype.setUserPreferences = function(prefs) { - this.prefs_ = prefs; + this.options_.prefs = prefs; return this; }; @@ -351,12 +533,42 @@ Options.prototype.setUserPreferences = function(prefs) { * @param {!webdriver.logging.Preferences} prefs The logging preferences. * @return {!Options} A self reference. */ -Options.prototype.setLoggingPreferences = function(prefs) { +Options.prototype.setLoggingPrefs = function(prefs) { this.logPrefs_ = prefs; return this; }; +/** + * Sets the performance logging preferences. Options include: + * + * - `enableNetwork`: Whether or not to collect events from Network domain. + * - `enablePage`: Whether or not to collect events from Page domain. + * - `enableTimeline`: Whether or not to collect events from Timeline domain. + * Note: when tracing is enabled, Timeline domain is implicitly disabled, + * unless `enableTimeline` is explicitly set to true. + * - `tracingCategories`: A comma-separated string of Chrome tracing categories + * for which trace events should be collected. An unspecified or empty + * string disables tracing. + * - `bufferUsageReportingInterval`: The requested number of milliseconds + * between DevTools trace buffer usage events. For example, if 1000, then + * once per second, DevTools will report how full the trace buffer is. If a + * report indicates the buffer usage is 100%, a warning will be issued. + * + * @param {{enableNetwork: boolean, + * enablePage: boolean, + * enableTimeline: boolean, + * tracingCategories: string, + * bufferUsageReportingInterval: number}} prefs The performance + * logging preferences. + * @return {!Options} A self reference. + */ +Options.prototype.setPerfLoggingPrefs = function(prefs) { + this.options_.perfLoggingPrefs = prefs; + return this; +}; + + /** * Sets preferences for the "Local State" file in Chrome's user data * directory. @@ -364,7 +576,87 @@ Options.prototype.setLoggingPreferences = function(prefs) { * @return {!Options} A self reference. */ Options.prototype.setLocalState = function(state) { - this.localState_ = state; + this.options_.localState = state; + return this; +}; + + +/** + * Sets the name of the activity hosting a Chrome-based Android WebView. This + * option must be set to connect to an [Android WebView]( + * https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android) + * + * @param {string} name The activity name. + * @return {!Options} A self reference. + */ +Options.prototype.androidActivity = function(name) { + this.options_.androidActivity = name; + return this; +}; + + +/** + * Sets the device serial number to connect to via ADB. If not specified, the + * ChromeDriver will select an unused device at random. An error will be + * returned if all devices already have active sessions. + * + * @param {string} serial The device serial number to connect to. + * @return {!Options} A self reference. + */ +Options.prototype.androidDeviceSerial = function(serial) { + this.options_.androidDeviceSerial = serial; + return this; +}; + + +/** + * Configures the ChromeDriver to launch Chrome on Android via adb. This + * function is shorthand for + * {@link #androidPackage options.androidPackage('com.android.chrome')}. + * @return {!Options} A self reference. + */ +Options.prototype.androidChrome = function() { + return this.androidPackage('com.android.chrome'); +}; + + +/** + * Sets the package name of the Chrome or WebView app. + * + * @param {?string} pkg The package to connect to, or `null` to disable Android + * and switch back to using desktop Chrome. + * @return {!Options} A self reference. + */ +Options.prototype.androidPackage = function(pkg) { + this.options_.androidPackage = pkg; + return this; +}; + + +/** + * Sets the process name of the Activity hosting the WebView (as given by `ps`). + * If not specified, the process name is assumed to be the same as + * {@link #androidPackage}. + * + * @param {string} processName The main activity name. + * @return {!Options} A self reference. + */ +Options.prototype.androidProcess = function(processName) { + this.options_.androidProcess = processName; + return this; +}; + + +/** + * Sets whether to connect to an already-running instead of the specified + * {@linkplain #androidProcess app} instead of launching the app with a clean + * data directory. + * + * @param {boolean} useRunning Whether to connect to a running instance. + * @return {!Options} A self reference. + */ +Options.prototype.androidUseRunningApp = function(useRunning) { + this.options_.androidUseRunningApp = useRunning; return this; }; @@ -376,14 +668,68 @@ Options.prototype.setLocalState = function(state) { * @return {!Options} A self reference. */ Options.prototype.setChromeLogFile = function(path) { - this.logFile_ = path; + this.options_.logPath = path; + return this; +}; + + +/** + * Sets the directory to store Chrome minidumps in. This option is only + * supported when ChromeDriver is running on Linux. + * @param {string} path The directory path. + * @return {!Options} A self reference. + */ +Options.prototype.setChromeMinidumpPath = function(path) { + this.options_.minidumpPath = path; + return this; +}; + + +/** + * Configures Chrome to emulate a mobile device. For more information, refer to + * the ChromeDriver project page on [mobile emulation][em]. Configuration + * options include: + * + * - `deviceName`: The name of a pre-configured [emulated device][devem] + * - `width`: screen width, in pixels + * - `height`: screen height, in pixels + * - `pixelRatio`: screen pixel ratio + * + * __Example 1: Using a Pre-configured Device__ + * + * var options = new chrome.Options().setMobileEmulation( + * {deviceName: 'Google Nexus 5'}); + * + * var driver = new chrome.Driver(options); + * + * __Example 2: Using Custom Screen Configuration__ + * + * var options = new chrome.Options().setMobileEmulation({ + * width: 360, + * height: 640, + * pixelRatio: 3.0 + * }); + * + * var driver = new chrome.Driver(options); + * + * + * [em]: https://sites.google.com/a/chromium.org/chromedriver/mobile-emulation + * [devem]: https://developer.chrome.com/devtools/docs/device-mode + * + * @param {?({deviceName: string}| + * {width: number, height: number, pixelRatio: number})} config The + * mobile emulation configuration, or `null` to disable emulation. + * @return {!Options} A self reference. + */ +Options.prototype.setMobileEmulation = function(config) { + this.options_.mobileEmulation = config; return this; }; /** * Sets the proxy settings for the new session. - * @param {ProxyConfig} proxy The proxy configuration to use. + * @param {webdriver.ProxyConfig} proxy The proxy configuration to use. * @return {!Options} A self reference. */ Options.prototype.setProxy = function(proxy) { @@ -414,59 +760,89 @@ Options.prototype.toCapabilities = function(opt_capabilities) { * @return {{args: !Array., * binary: (string|undefined), * detach: boolean, - * extensions: !Array., + * extensions: !Array.<(string|!webdriver.promise.Promise.)>, * localState: (Object|undefined), - * logFile: (string|undefined), + * logPath: (string|undefined), * prefs: (Object|undefined)}} The JSON wire protocol representation * of this instance. + * @override */ -Options.prototype.toJSON = function() { - return { - args: this.args_, - binary: this.binary_, - detach: !!this.detach_, - extensions: this.extensions_.map(function(extension) { +Options.prototype.serialize = function() { + var json = {}; + for (var key in this.options_) { + if (this.options_[key] != null) { + json[key] = this.options_[key]; + } + } + if (this.extensions_.length) { + json.extensions = this.extensions_.map(function(extension) { if (Buffer.isBuffer(extension)) { return extension.toString('base64'); } - return fs.readFileSync(extension, 'base64'); - }), - localState: this.localState_, - logFile: this.logFile_, - prefs: this.prefs_ - }; + return webdriver.promise.checkedNodeCall( + fs.readFile, extension, 'base64'); + }); + } + return json; }; /** - * Creates a new ChromeDriver session. - * @param {(webdriver.Capabilities|Options)=} opt_options The session options. + * Creates a new WebDriver client for Chrome. + * + * @param {(webdriver.Capabilities|Options)=} opt_config The configuration + * options. * @param {remote.DriverService=} opt_service The session to use; will use - * the {@link getDefaultService default service} by default. - * @return {!webdriver.WebDriver} A new WebDriver instance. + * the {@linkplain #getDefaultService default service} by default. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or + * {@code null} to use the currently active flow. + * @constructor + * @extends {webdriver.WebDriver} */ -function createDriver(opt_options, opt_service) { +var Driver = function(opt_config, opt_service, opt_flow) { var service = opt_service || getDefaultService(); - var executor = executors.createExecutor(service.start()); + var executor = createExecutor(service.start()); - var options = opt_options || new Options(); - if (opt_options instanceof webdriver.Capabilities) { - // Extract the Chrome-specific options so we do not send unnecessary - // data across the wire. - options = Options.fromCapabilities(options); - } + var capabilities = + opt_config instanceof Options ? opt_config.toCapabilities() : + (opt_config || webdriver.Capabilities.chrome()); + + var driver = webdriver.WebDriver.createSession( + executor, capabilities, opt_flow); + + webdriver.WebDriver.call( + this, driver.getSession(), executor, driver.controlFlow()); +}; +util.inherits(Driver, webdriver.WebDriver); - return webdriver.WebDriver.createSession( - executor, options.toCapabilities()); -} +/** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ +Driver.prototype.setFileDetector = function() { +}; + + +/** + * Schedules a command to launch Chrome App with given ID. + * @param {string} id ID of the App to launch. + * @return {!webdriver.promise.Promise} A promise that will be resolved + * when app is launched. + */ +Driver.prototype.launchApp = function(id) { + return this.schedule( + new webdriver.Command(Command.LAUNCH_APP).setParameter('id', id), + 'Driver.launchApp()'); +}; // PUBLIC API -exports.ServiceBuilder = ServiceBuilder; +exports.Driver = Driver; exports.Options = Options; -exports.createDriver = createDriver; +exports.ServiceBuilder = ServiceBuilder; exports.getDefaultService = getDefaultService; exports.setDefaultService = setDefaultService; diff --git a/docs/Changes.html b/docs/Changes.html new file mode 100644 index 0000000..c0558f8 --- /dev/null +++ b/docs/Changes.html @@ -0,0 +1,351 @@ +Changes

v2.48.0

+
  • +

    Node v0.12.x users must run with --harmony. This is the last release that +will support v0.12.x

    +
  • +

    FIXED: (Promise/A+ compliance) When a promise is rejected with a thenable, +the promise adopts the thenable as its rejection reason instead of waiting +for it to settle. The previous (incorrect) behavior was hidden by bugs in +the promises-aplus-tests compliance test suite that were fixed in version +2.1.1.

    +
  • +

    FIXED: the webdriver.promise.ControlFlow now has a consistent execution +order for tasks/callbacks scheduled in different turns of the JS event loop. +Refer to the webdriver.promise documentation for more details.

    +
  • +

    FIXED: do not drop user auth from the WebDriver server URL.

    +
  • +

    FIXED: a single firefox.Binary instance may be used to configure and +launch multiple FirefoxDriver sessions.

    +
    var binary = new firefox.Binary();
    +var options = new firefox.Options().setBinary(binary);
    +var builder = new Builder().setFirefoxOptions(options);
    +
    +var driver1 = builder.build();
    +var driver2 = builder.build();
    +
    +
  • +

    FIXED: zip files created for transfer to a remote WebDriver server are no +longer compressed. If the zip contained a file that was already compressed, +the server would return an "invalid code lengths set" error.

    +
  • +

    FIXED: Surfaced the loopback option to remote/SeleniumServer. When set, +the server will be accessed using the current host's loopback address.

    +
+

v2.47.0

+

Notice

+

This is the last release for selenium-webdriver that will support ES5. +Subsequent releases will depend on ES6 features that are enabled by +default in Node v4.0.0. Node v0.12.x will +continue to be supported, but will require setting the --harmony flag.

+

Change Summary

+
  • Add support for Node v4.0.0 +
    • Updated ws dependency from 0.7.1 to 0.8.0
    +
  • Bumped the minimum supported version of Node from 0.10.x to 0.12.x. This +is in accordance with the Node support policy established in v2.45.0.
+

v2.46.1

+
  • Fixed internal module loading on Windows.
  • Fixed error message format on timeouts for until.elementLocated() +and until.elementsLocated().
+

v2.46.0

+
  • Exposed a new logging API via the webdriver.logging module. For usage, see +example/logging.js.
  • Added support for using a proxy server for WebDriver commands. +See Builder#usingWebDriverProxy() for more info.
  • Removed deprecated functions: +
    • Capabilities#toJSON()
    • UnhandledAlertError#getAlert()
    • chrome.createDriver()
    • phantomjs.createDriver()
    • promise.ControlFlow#annotateError()
    • promise.ControlFlow#await()
    • promise.ControlFlow#clearHistory()
    • promise.ControlFlow#getHistory()
    +
  • Removed deprecated enum values: ErrorCode.NO_MODAL_DIALOG_OPEN and +ErrorCode.MODAL_DIALOG_OPENED. Use ErrorCode.NO_SUCH_ALERT and +ErrorCode.UNEXPECTED_ALERT_OPEN, respectively.
  • FIXED: The promise.ControlFlow will maintain state for promise chains +generated in a loop.
  • FIXED: Correct serialize target elements used in an action sequence.
  • FIXED: promise.ControlFlow#wait() now has consistent semantics for an +omitted or 0-timeout: it will wait indefinitely.
  • FIXED: remote.DriverService#start() will now fail if the child process dies +while waiting for the server to start accepting requests. Previously, start +would continue to poll the server address until the timeout expired.
  • FIXED: Skip launching Firefox with the -silent flag to preheat the profile. +Starting with Firefox 38, this would cause the browser to crash. This step, +which was first introduced for Selenium's java client back with Firefox 2, +no longer appears to be required.
  • FIXED: 8564: firefox.Driver#quit() will wait for the Firefox process to +terminate before deleting the temporary webdriver profile. This eliminates a +race condition where Firefox would write profile data during shutdown, +causing the rm -rf operation on the profile directory to fail.
+

v2.45.1

+
  • FIXED: 8548: Task callbacks are once again dropped if the task was cancelled +due to a previously uncaught error within the frame.
  • FIXED: 8496: Extended the chrome.Options API to cover all configuration +options (e.g. mobile emulation and performance logging) documented on the +ChromeDriver project site.
+

v2.45.0

+

Important Policy Change

+

Starting with the 2.45.0 release, selenium-webdriver will support the last +two stable minor releases for Node. For 2.45.0, this means Selenium will +support Node 0.10.x and 0.12.x. Support for the intermediate, un-stable release +(0.11.x) is "best-effort". This policy will be re-evaluated once Node has a +major version release (i.e. 1.0.0).

+

Change Summary

+
  • +

    Added native browser support for Internet Explorer, Opera 26+, and Safari

    +
  • +

    With the release of Node 0.12.0 +(finally!), the minimum supported version of Node is now 0.10.x.

    +
  • +

    The promise module is now Promises/A+ +compliant. The biggest compliance change is that promise callbacks are now +invoked in a future turn of the JS event loop. For example:

    +
      var promise = require('selenium-webdriver').promise;
    +  console.log('start');
    +  promise.fulfilled().then(function() {
    +    console.log('middle');
    +  });
    +  console.log('end');
    +
    +  // Output in selenium-webdriver@2.44.0
    +  // start
    +  // middle
    +  // end
    +  //
    +  // Output in selenium-webdriver@2.45.0
    +  // start
    +  // end
    +  // middle
    +
    +

    The promise.ControlFlow class has been updated to track the asynchronous +breaks required by Promises/A+, so there are no changes to task execution +order.

    +
  • +

    Updated how errors are annotated on failures. When a task fails, the +stacktrace from when that task was scheduled is appended to the rejection +reason with a From: prefix (if it is an Error object). For example:

    +
      var driver = new webdriver.Builder().forBrowser('chrome').build();
    +  driver.get('http://www.google.com/ncr');
    +  driver.call(function() {
    +    driver.wait(function() {
    +      return driver.isElementPresent(webdriver.By.id('not-there'));
    +    }, 2000, 'element not found');
    +  });
    +
    +

    This code will fail an error like:

    +
      Error: element not found
    +  Wait timed out after 2002ms
    +      at <stack trace>
    +  From: Task: element not found
    +      at <stack trace>
    +  From: Task: WebDriver.call(function)
    +      at <stack trace>
    +
    +
  • +

    Changed the format of strings returned by promise.ControlFlow#getSchedule. +This function now accepts a boolean to control whether the returned string +should include the stacktraces for when each task was scheduled.

    +
  • +

    Deprecating promise.ControlFlow#getHistory, +promise.ControlFlow#clearHistory, and promise.ControlFlow#annotateError. +These functions were all intended for internal use and are no longer +necessary, so they have been made no-ops.

    +
  • +

    WebDriver.wait() may now be used to wait for a promise to resolve, with +an optional timeout. Refer to the API documentation for more information.

    +
  • +

    Added support for copying files to a remote Selenium via sendKeys to test +file uploads. Refer to the API documentation for more information. Sample +usage included in test/upload_test.js

    +
  • +

    Expanded the interactions API to include touch actions. +See WebDriver.touchActions().

    +
  • +

    FIXED: 8380: firefox.Driver will delete its temporary profile on quit.

    +
  • +

    FIXED: 8306: Stack overflow in promise callbacks eliminated.

    +
  • +

    FIXED: 8221: Added support for defining custom command mappings. Includes +support for PhantomJS's executePhantomJS (requires PhantomJS 1.9.7 or +GhostDriver 1.1.0).

    +
  • +

    FIXED: 8128: When the FirefoxDriver marshals an object to the page for +executeScript, it defines additional properties (required by the driver's +implementation). These properties will no longer be enumerable and should +be omitted (i.e. they won't show up in JSON.stringify output).

    +
  • +

    FIXED: 8094: The control flow will no longer deadlock when a task returns +a promise that depends on the completion of sub-tasks.

    +
+

v2.44.0

+
  • +

    Added the until module, which defines common explicit wait conditions. +Sample usage:

    +
      var firefox = require('selenium-webdriver/firefox'),
    +      until = require('selenium-webdriver/until');
    +
    +  var driver = new firefox.Driver();
    +  driver.get('http://www.google.com/ncr');
    +  driver.wait(until.titleIs('Google Search'), 1000);
    +
    +
  • +

    FIXED: 8000: Builder.forBrowser() now accepts an empty string since some +WebDriver implementations ignore the value. A value must still be specified, +however, since it is a required field in WebDriver's wire protocol.

    +
  • +

    FIXED: 7994: The stacktrace module will not modify stack traces if the +initial parse fails (e.g. the user defined Error.prepareStackTrace)

    +
  • +

    FIXED: 5855: Added a module (until) that defines several common conditions +for use with explicit waits. See updated examples for usage.

    +
+

v2.43.5

+
  • FIXED: 7905: Builder.usingServer(url) once again returns this for +chaining.
+

v2.43.2-4

+
  • No changes; version bumps while attempting to work around an issue with +publishing to npm (a version string may only be used once).
+

v2.43.1

+
  • Fixed an issue with flakiness when setting up the Firefox profile that could +prevent the driver from initializing properly.
+

v2.43.0

+
  • +

    Added native support for Firefox - the Java Selenium server is no longer +required.

    +
  • +

    Added support for generator functions to ControlFlow#execute and +ControlFlow#wait. For more information, see documentation on +webdriver.promise.consume. Requires harmony support (run with +node --harmony-generators in v0.11.x).

    +
  • +

    Various improvements to the Builder API. Notably, the build() function +will no longer default to attempting to use a server at +http://localhost:4444/wd/hub if it cannot start a browser directly - +you must specify the WebDriver server with usingServer(url). You can +also set the target browser and WebDriver server through a pair of +environment variables. See the documentation on the Builder constructor +for more information.

    +
  • +

    For consistency with the other language bindings, added browser specific +classes that can be used to start a browser without the builder.

    +
      var webdriver = require('selenium-webdriver')
    +      chrome = require('selenium-webdriver/chrome');
    +
    +  // The following are equivalent.
    +  var driver1 = new webdriver.Builder().forBrowser('chrome').build();
    +  var driver2 = new chrome.Driver();
    +
    +
  • +

    Promise A+ compliance: a promise may no longer resolve to itself.

    +
  • +

    For consistency with other language bindings, deprecated +UnhandledAlertError#getAlert and added #getAlertText. +getAlert will be removed in 2.45.0.

    +
  • +

    FIXED: 7641: Deprecated ErrorCode.NO_MODAL_DIALOG_OPEN and +ErrorCode.MODAL_DIALOG_OPENED in favor of the new +ErrorCode.NO_SUCH_ALERT and ErrorCode.UNEXPECTED_ALERT_OPEN, +respectively.

    +
  • +

    FIXED: 7563: Mocha integration no longer disables timeouts. Default Mocha +timeouts apply (2000 ms) and may be changed using this.timeout(ms).

    +
  • +

    FIXED: 7470: Make it easier to create WebDriver instances in custom flows for +parallel execution.

    +
+

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 +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
    +
    +
+
\ No newline at end of file diff --git a/docs/class_bot_Error.html b/docs/class_bot_Error.html index 2171f78..80d2347 100644 --- a/docs/class_bot_Error.html +++ b/docs/class_bot_Error.html @@ -1,6 +1,15 @@ -bot.Error

Class bot.Error

code »
Error
-  └ bot.Error

Error extension that includes error status codes from the WebDriver wire - protocol: - http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes

Constructor

bot.Error ( code, opt_message )
Parameters
code: !bot.ErrorCode
The error's status code.
opt_message: string=
Optional error message.

Enumerations

bot.Error.State
Status strings enumerated in the W3C WebDriver working draft.
Show:

Instance Methods

Defined in bot.Error

Returns
he string representation of this error.

Instance Properties

Defined in bot.Error

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.

Static Properties

A map of error codes to state string.

\ No newline at end of file +Error

class Error

Error
+  └ bot.Error

Represents an error returned from a WebDriver command request.

+

new Error(code, opt_message)

Parameters
codenumber

The error's status code.

+
opt_messagestring=

Optional error message.

+

Instance Methods

toString()code »

Returns
string

The string representation of this error.

+

Instance Properties

codenumber

This error's status code.

+
descriptionstring

IE-only.

+
fileNamestring

Mozilla-only

+
isAutomationErrorboolean

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.

+
lineNumbernumber

Mozilla-only.

+
messagestring
No description.
namestring
No description.
sourceURL?

Doesn't seem to exist, but closure/debug.js references it.

+
stackstring
No description.
statestring
No description.

Types

Error.State

Status strings enumerated in the W3C WebDriver protocol.

+
\ No newline at end of file diff --git a/docs/class_goog_Disposable.html b/docs/class_goog_Disposable.html new file mode 100644 index 0000000..0ab639e --- /dev/null +++ b/docs/class_goog_Disposable.html @@ -0,0 +1,37 @@ +goog.Disposable

Class goog.Disposable

code »
All implemented interfaces:
goog.disposable.IDisposable

Class that provides the basic implementation for disposable objects. If your + class holds one or more references to COM objects, DOM nodes, or other + disposable objects, it should extend this class or implement the disposable + interface (defined in goog.disposable.IDisposable).

Constructor

goog.Disposable ( )

Enumerations

Show:

Instance Methods

code »<T> addOnDisposeCallback ( callback, opt_scope )

Invokes a callback function when this object is disposed. Callbacks are + invoked in the order in which they were added.

Parameters
callback: function(this: T): ?
The callback function.
opt_scope: T=
An optional scope to call the callback in.
code »dispose ( )void

Disposes of the object. If the object hasn't already been disposed of, calls + #disposeInternal. Classes that extend goog.Disposable should + override #disposeInternal in order to delete references to COM + objects, DOM nodes, and other disposable objects. Reentrant.

Returns
Nothing.

Deletes or nulls out any references to COM objects, DOM nodes, or other + disposable objects. Classes that extend goog.Disposable should + override this method. + Not reentrant. To avoid calling it twice, it must only be called from the + subclass' disposeInternal method. Everywhere else the public + dispose method must be used. + For example: +

+   mypackage.MyClass = function() {
+     mypackage.MyClass.base(this, 'constructor');
+     // Constructor logic specific to MyClass.
+     ...
+   };
+   goog.inherits(mypackage.MyClass, goog.Disposable);
+
+   mypackage.MyClass.prototype.disposeInternal = function() {
+     // Dispose logic specific to MyClass.
+     ...
+     // Call superclass's disposeInternal at the end of the subclass's, like
+     // in C++, to avoid hard-to-catch issues.
+     mypackage.MyClass.base(this, 'disposeInternal');
+   };
+ 
Deprecated: Use #isDisposed instead.
Returns
Whether the object has been disposed of.
Returns
Whether the object has been disposed of.

Associates a disposable object with this object so that they will be disposed + together.

Parameters
disposable: goog.disposable.IDisposable
that will be disposed when + this object is disposed.

Instance Properties

If monitoring the goog.Disposable instances is enabled, stores the creation + stack trace of the Disposable instance.

Whether the object has been disposed of.

Callbacks to invoke when this object is disposed.

Static Functions

Clears the registry of undisposed objects but doesn't dispose of them.

Returns
All goog.Disposable objects that + haven't been disposed of.

Returns True if we can verify the object is disposed. + Calls isDisposed on the argument if it supports it. If obj + is not an object with an isDisposed() method, return false.

Parameters
obj: *
The object to investigate.
Returns
True if we can verify the object is disposed.

Static Properties

Maps the unique ID of every undisposed goog.Disposable object to + the object itself.

Compiler Constants

\ No newline at end of file diff --git a/docs/class_goog_Uri.html b/docs/class_goog_Uri.html index b7fd597..1474bcb 100644 --- a/docs/class_goog_Uri.html +++ b/docs/class_goog_Uri.html @@ -1,8 +1,14 @@ -goog.Uri

Class goog.Uri

code »

This class contains setters and getters for the parts of the URI. +goog.Uri

Class goog.Uri

code »

This class contains setters and getters for the parts of the URI. The getXyz/setXyz methods return the decoded part -- sogoog.Uri.parse('/foo%20bar').getPath() will return the decoded path, /foo bar. + Reserved characters (see RFC 3986 section 2.2) can be present in + their percent-encoded form in scheme, domain, and path URI components and + will not be auto-decoded. For example: + goog.Uri.parse('rel%61tive/path%2fto/resource').getPath() will + return relative/path%2fto/resource. + 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. @@ -11,16 +17,16 @@ goog.Uri.parse('/foo').setFragment('part').toString().

Constructor

goog.Uri ( opt_uri, opt_ignoreCase )
Parameters
opt_uri: *=
Optional string URI to parse (use goog.Uri.create() to create a URI from parts), or if a goog.Uri is passed, a clone is created.
opt_ignoreCase: boolean=
If true, #getParameterValue will ignore - the case of the parameter name.

Classes

goog.Uri.QueryData
Class used to represent URI query parameters.
Show:

Instance Methods

Clones the URI instance.

Returns
New instance of the URI objcet.

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 decoded URI query, not including the ?.
Returns
The decoded domain.
Returns
The encoded URI query, not including the ?.
Returns
The URI fragment, not including the #.
Returns
Whether to ignore case.

Returns the first value for a given cgi parameter or undefined if the given + the case of the parameter name.

Classes

goog.Uri.QueryData
Class used to represent URI query parameters.
Show:

Instance Methods

Clones the URI instance.

Returns
New instance of the URI object.

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 decoded URI query, not including the ?.
Returns
The decoded domain.
Returns
The encoded URI query, not including the ?.
Returns
The URI fragment, not including the #.
Returns
Whether to ignore case.

Returns the first value for a given cgi parameter or undefined if the given parameter name does not appear in the query string.

Parameters
paramName: string
Unescaped parameter name.
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 + string.

Returns the values for a given cgi parameter as a list of decoded query parameter values.

Parameters
name: string
The parameter to get values for.
Returns
The values for a given cgi parameter as a list of - decoded query parameter values.
Returns
The decoded path.
Returns
The port number.
Returns
The encoded URI query, not including the ?. + decoded query parameter values.
Returns
The decoded path.
Returns
The port number.
Returns
The encoded URI query, not including the ?. Warning: This method, unlike other getter methods, returns encoded - value, instead of decoded one.

Returns the query data.

Returns
QueryData object.
Returns
The encoded scheme/protocol for the URI.
Returns
The decoded user info.
Returns
Whether the domain has been set.
Returns
Whether the URI has a fragment set.
Returns
Whether the path has been set.
Returns
Whether the port has been set.
Returns
Whether the query string has been set.

Returns true if this has the same domain as that of uri2.

Parameters
uri2: goog.Uri
The URI object to compare to.
Returns
true if same domain; false otherwise.
Returns
Whether the scheme has been set.
Returns
Whether the user info has been set.
Returns
Whether the URI is read only.

Adds a random parameter to the Uri.

Returns
Reference to this Uri object.

Removes the named query parameter.

Parameters
key: string
The parameter to remove.
Returns
Reference to this URI object.
code »resolve ( relativeUri )!goog.Uri

Resolves the given relative URI (a goog.Uri object), using the URI + value, instead of decoded one.

Returns the query data.

Returns
QueryData object.
Returns
The encoded scheme/protocol for the URI.
Returns
The decoded user info.
Returns
Whether the domain has been set.
Returns
Whether the URI has a fragment set.
Returns
Whether the path has been set.
Returns
Whether the port has been set.
Returns
Whether the query string has been set.

Returns true if this has the same domain as that of uri2.

Parameters
uri2: goog.Uri
The URI object to compare to.
Returns
true if same domain; false otherwise.
Returns
Whether the scheme has been set.
Returns
Whether the user info has been set.
Returns
Whether the URI is read only.

Adds a random parameter to the Uri.

Returns
Reference to this Uri object.

Removes the named query parameter.

Parameters
key: string
The parameter to remove.
Returns
Reference to this URI object.
code »resolve ( relativeUri )!goog.Uri

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:
@@ -31,27 +37,29 @@ 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
relativeUri: goog.Uri
The relative URI to resolve.
Returns
The resolved URI.
code »setDomain ( newDomain, opt_decode )!goog.Uri

Sets the domain.

Parameters
newDomain: string
New domain value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setFragment ( newFragment, opt_decode )!goog.Uri

Sets the URI fragment.

Parameters
newFragment: string
New fragment value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setIgnoreCase ( ignoreCase )!goog.Uri

Sets whether to ignore case. + segments will be resolved, as described in RFC 3986.

Parameters
relativeUri: goog.Uri
The relative URI to resolve.
Returns
The resolved URI.
code »setDomain ( newDomain, opt_decode )!goog.Uri

Sets the domain.

Parameters
newDomain: string
New domain value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setFragment ( newFragment, opt_decode )!goog.Uri

Sets the URI fragment.

Parameters
newFragment: string
New fragment value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setIgnoreCase ( ignoreCase )!goog.Uri

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.

Parameters
ignoreCase: boolean
whether this goog.Uri should ignore case.
Returns
Reference to this Uri object.

Sets the value of the named query parameters, clearing previous values for - that key.

Parameters
key: string
The parameter to set.
value: *
The new value.
Returns
Reference to this URI object.
code »setParameterValues ( key, values )!goog.Uri

Sets the values of the named query parameters, clearing previous values for + ignoreCase_ is set to false, the keys will all be lower-cased.

Parameters
ignoreCase: boolean
whether this goog.Uri should ignore case.
Returns
Reference to this Uri object.

Sets the value of the named query parameters, clearing previous values for + that key.

Parameters
key: string
The parameter to set.
value: *
The new value.
Returns
Reference to this URI object.
code »setParameterValues ( key, values )!goog.Uri

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, goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new']) yields foo?a=b&e=f&c=new.

Parameters
key: string
The parameter to set.
values: *
The new values. If values is a single - string then it will be treated as the sole value.
Returns
Reference to this URI object.
code »setPath ( newPath, opt_decode )!goog.Uri

Sets the path.

Parameters
newPath: string
New path value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setPort ( newPort )!goog.Uri

Sets the port number.

Parameters
newPort: *
Port number. Will be explicitly casted to a number.
Returns
Reference to this URI object.
code »setQuery ( newQuery, opt_decode )!goog.Uri

Sets the URI query.

Parameters
newQuery: string
New query value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setQueryData ( queryData, opt_decode )!goog.Uri

Sets the query data.

Parameters
queryData: (goog.Uri.QueryData|string|undefined)
QueryData object.
opt_decode: boolean=
Optional param for whether to decode new value. - Applies only if queryData is a string.
Returns
Reference to this URI object.
code »setReadOnly ( isReadOnly )!goog.Uri

Sets whether Uri is read only. If this goog.Uri is read-only, + string then it will be treated as the sole value.Returns

Reference to this URI object.
code »setPath ( newPath, opt_decode )!goog.Uri

Sets the path.

Parameters
newPath: string
New path value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setPort ( newPort )!goog.Uri

Sets the port number.

Parameters
newPort: *
Port number. Will be explicitly casted to a number.
Returns
Reference to this URI object.
code »setQuery ( newQuery, opt_decode )!goog.Uri

Sets the URI query.

Parameters
newQuery: string
New query value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setQueryData ( queryData, opt_decode )!goog.Uri

Sets the query data.

Parameters
queryData: (goog.Uri.QueryData|string|undefined)
QueryData object.
opt_decode: boolean=
Optional param for whether to decode new value. + Applies only if queryData is a string.
Returns
Reference to this URI object.
code »setReadOnly ( isReadOnly )!goog.Uri

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.

Parameters
isReadOnly: boolean
whether this goog.Uri should be read only.
Returns
Reference to this Uri object.
code »setScheme ( newScheme, opt_decode )!goog.Uri

Sets the scheme/protocol.

Parameters
newScheme: string
New scheme value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setUserInfo ( newUserInfo, opt_decode )!goog.Uri

Sets the userInfo.

Parameters
newUserInfo: string
New userInfo value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
Returns
The string form of the url.

Instance Properties

Domain part, e.g. "www.google.com".

The fragment without the #.

Whether or not to ignore case when comparing query params.

Whether or not this Uri should be treated as Read Only.

Path, e.g. "/tests/img.png".

Port, e.g. 8080.

Object representing query data.

Scheme such as "http".

User credentials in the form "username:password".

Static Functions

code »goog.Uri.create ( opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query, opt_fragment, opt_ignoreCase )!goog.Uri

Creates a new goog.Uri object from unencoded parts.

Parameters
opt_scheme: ?string=
Scheme/protocol or full URI to parse.
opt_userInfo: ?string=
username:password.
opt_domain: ?string=
www.google.com.
opt_port: ?number=
9830.
opt_path: ?string=
/some/path/to/a/file.html.
opt_query: (string|goog.Uri.QueryData)=
a=1&b=2.
opt_fragment: ?string=
The fragment without the #.
opt_ignoreCase: boolean=
Whether to ignore parameter name case in - #getParameterValue.
Returns
The new URI object.

Decodes a value or returns the empty string if it isn't defined or empty.

Parameters
val: (string|undefined)
Value to decode.
Returns
Decoded value.

Converts a character in [\01-\177] to its unicode character equivalent.

Parameters
ch: string
One character string.
Returns
Encoded string.
code »goog.Uri.encodeSpecialChars_ ( unescapedPart, extra )?string

If unescapedPart is non null, then escapes any characters in it that aren't + this Uri.

Parameters
isReadOnly: boolean
whether this goog.Uri should be read only.
Returns
Reference to this Uri object.
code »setScheme ( newScheme, opt_decode )!goog.Uri

Sets the scheme/protocol.

Parameters
newScheme: string
New scheme value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
code »setUserInfo ( newUserInfo, opt_decode )!goog.Uri

Sets the userInfo.

Parameters
newUserInfo: string
New userInfo value.
opt_decode: boolean=
Optional param for whether to decode new value.
Returns
Reference to this URI object.
Returns
The string form of the url.

Instance Properties

Domain part, e.g. "www.google.com".

The fragment without the #.

Whether or not to ignore case when comparing query params.

Whether or not this Uri should be treated as Read Only.

Path, e.g. "/tests/img.png".

Port, e.g. 8080.

Object representing query data.

Scheme such as "http".

User credentials in the form "username:password".

Static Functions

code »goog.Uri.create ( opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query, opt_fragment, opt_ignoreCase )!goog.Uri

Creates a new goog.Uri object from unencoded parts.

Parameters
opt_scheme: ?string=
Scheme/protocol or full URI to parse.
opt_userInfo: ?string=
username:password.
opt_domain: ?string=
www.google.com.
opt_port: ?number=
9830.
opt_path: ?string=
/some/path/to/a/file.html.
opt_query: (string|goog.Uri.QueryData)=
a=1&b=2.
opt_fragment: ?string=
The fragment without the #.
opt_ignoreCase: boolean=
Whether to ignore parameter name case in + #getParameterValue.
Returns
The new URI object.
code »goog.Uri.decodeOrEmpty_ ( val, opt_preserveReserved )string

Decodes a value or returns the empty string if it isn't defined or empty.

Parameters
val: (string|undefined)
Value to decode.
opt_preserveReserved: boolean=
If true, restricted characters will + not be decoded.
Returns
Decoded value.

Converts a character in [\01-\177] to its unicode character equivalent.

Parameters
ch: string
One character string.
Returns
Encoded string.
code »goog.Uri.encodeSpecialChars_ ( unescapedPart, extra, opt_removeDoubleEncoding )?string

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.

Parameters
unescapedPart: *
The string to encode.
extra: RegExp
A character set of characters in [\01-\177].
Returns
null iff unescapedPart == null.
code »goog.Uri.haveSameDomain ( uri1String, uri2String )boolean

Checks whether two URIs have the same domain.

Parameters
uri1String: string
First URI string.
uri2String: string
Second URI string.
Returns
true if the two URIs have the same domain; false otherwise.
code »goog.Uri.parse ( uri, opt_ignoreCase )!goog.Uri

Creates a uri from the string form. Basically an alias of new goog.Uri(). + appear in extra.

Parameters
unescapedPart: *
The string to encode.
extra: RegExp
A character set of characters in [\01-\177].
opt_removeDoubleEncoding: boolean=
If true, remove double percent + encoding.
Returns
null iff unescapedPart == null.
code »goog.Uri.haveSameDomain ( uri1String, uri2String )boolean

Checks whether two URIs have the same domain.

Parameters
uri1String: string
First URI string.
uri2String: string
Second URI string.
Returns
true if the two URIs have the same domain; false otherwise.
code »goog.Uri.parse ( uri, opt_ignoreCase )!goog.Uri

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.

Parameters
uri: *
Raw URI string or instance of Uri object.
opt_ignoreCase: boolean=
Whether to ignore the case of parameter - names in #getParameterValue.
Returns
The new URI object.

Removes dot segments in given path component, as described in - RFC 3986, section 5.2.4.

Parameters
path: string
A non-empty path component.
Returns
Path component with removed dot segments.

Resolves a relative Uri against a base Uri, accepting both strings and - Uri objects.

Parameters
base: *
Base Uri.
rel: *
Relative Uri.
Returns
Resolved uri.

Static Properties

Parameter name added to stop caching.

If true, we preserve the type of query parameters set programmatically. + names in #getParameterValue.Returns

The new URI object.

Removes dot segments in given path component, as described in + RFC 3986, section 5.2.4.

Parameters
path: string
A non-empty path component.
Returns
Path component with removed dot segments.
code »goog.Uri.removeDoubleEncoding_ ( doubleEncodedString )string

Removes double percent-encoding from a string.

Parameters
doubleEncodedString: string
String
Returns
String with double encoding removed.

Resolves a relative Uri against a base Uri, accepting both strings and + Uri objects.

Parameters
base: *
Base Uri.
rel: *
Relative Uri.
Returns
Resolved uri.

Static Properties

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. @@ -59,5 +67,6 @@ 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.

\ No newline at end of file + 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. + Colon is included due to RFC 3986 3.3.

Regular expression for characters that are disallowed in the scheme or + userInfo part of the URI.

\ No newline at end of file diff --git a/docs/class_goog_Uri_QueryData.html b/docs/class_goog_Uri_QueryData.html index 2af4722..f24f8dd 100644 --- a/docs/class_goog_Uri_QueryData.html +++ b/docs/class_goog_Uri_QueryData.html @@ -1,34 +1,34 @@ -goog.Uri.QueryData

Class goog.Uri.QueryData

code »

Class used to represent URI query parameters. It is essentially a hash of +goog.Uri.QueryData

Class goog.Uri.QueryData

code »

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.

Constructor

goog.Uri.QueryData ( opt_query, opt_uri, opt_ignoreCase )
Parameters
opt_query: ?string=
Optional encoded query string to parse into the object.
opt_uri: goog.Uri=
Optional uri object that should have its cache invalidated when this object updates. Deprecated -- this is no longer required.
opt_ignoreCase: boolean=
If true, ignore the case of the parameter - name in #get.
Show:

Instance Methods

code »add ( key, value )!goog.Uri.QueryData

Adds a key value pair.

Parameters
key: string
Name.
value: *
Value.
Returns
Instance of this object.

Clears the parameters.

Clone the query data instance.

Returns
New instance of the QueryData object.

Whether there is a parameter with the given name

Parameters
key: string
The parameter name to check for.
Returns
Whether there is a parameter with the given name.

Whether there is a parameter with the given value.

Parameters
value: *
The value to check for.
Returns
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.

code »extend ( var_args )

Extends a query data object with another query data or map like object. This + name in #get.

Show:

Instance Methods

code »add ( key, value )!goog.Uri.QueryData

Adds a key value pair.

Parameters
key: string
Name.
value: *
Value.
Returns
Instance of this object.

Clears the parameters.

Clone the query data instance.

Returns
New instance of the QueryData object.

Whether there is a parameter with the given name

Parameters
key: string
The parameter name to check for.
Returns
Whether there is a parameter with the given name.

Whether there is a parameter with the given value.

Parameters
value: *
The value to check for.
Returns
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.

code »extend ( var_args )

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.

Parameters
var_args: ...(goog.Uri.QueryData|goog.structs.Map|Object)
The object - from which key value pairs will be copied.

Removes all keys that are not in the provided list. (Modifies this object.)

Parameters
keys: Array.<string>
The desired keys.
Returns
a reference to this object.
code »get ( key, opt_default )*

Returns the first value associated with the key. If the query data has no + from which key value pairs will be copied.

Removes all keys that are not in the provided list. (Modifies this object.)

Parameters
keys: Array.<string>
The desired keys.
Returns
a reference to this object.
code »get ( key, opt_default )*

Returns the first value associated with the key. If the query data has no such key this will return undefined or the optional default.

Parameters
key: string
The name of the parameter to get the value for.
opt_default: *=
The default value to return if the query data has no such key.
Returns
The first string value associated with the key, or opt_default - if there's no value.
Returns
The number of parameters.

Helper function to get the key name from a JavaScript object. Converts - the object to a string, and to lower case if necessary.

Parameters
arg: *
The object to get a key name from.
Returns
valid key name which can be looked up in #keyMap_.

Returns all the keys of the parameters. If a key is used multiple times - it will be included multiple times in the returned array

Returns
All the keys of the parameters.
code »getValues ( opt_key )!Array

Returns all the values of the parameters with the given name. If the query + if there's no value.

Returns
The number of parameters.

Helper function to get the key name from a JavaScript object. Converts + the object to a string, and to lower case if necessary.

Parameters
arg: *
The object to get a key name from.
Returns
valid key name which can be looked up in #keyMap_.

Returns all the keys of the parameters. If a key is used multiple times + it will be included multiple times in the returned array

Returns
All the keys of the parameters.
code »getValues ( opt_key )!Array

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.

Parameters
opt_key: string=
The name of the parameter to get the values for.
Returns
All the values of the parameters with the given name.

Invalidate the cache.

Returns
Whether we have any parameters.

Removes all the params with the given key.

Parameters
key: string
Name.
Returns
Whether any parameter was removed.
code »set ( key, value )!goog.Uri.QueryData

Sets a key value pair and removes all other keys with the same value.

Parameters
key: string
Name.
value: *
Value.
Returns
Instance of this object.
code »setIgnoreCase ( ignoreCase )

Ignore case in parameter names. + all values wil be returned.

Parameters
opt_key: string=
The name of the parameter to get the values for.
Returns
All the values of the parameters with the given name.

Invalidate the cache.

Returns
Whether we have any parameters.

Removes all the params with the given key.

Parameters
key: string
Name.
Returns
Whether any parameter was removed.
code »set ( key, value )!goog.Uri.QueryData

Sets a key value pair and removes all other keys with the same value.

Parameters
key: string
Name.
value: *
Value.
Returns
Instance of this object.
code »setIgnoreCase ( ignoreCase )

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.

Parameters
ignoreCase: boolean
whether this goog.Uri should ignore case.
code »setValues ( key, values )

Sets the values for a key. If the key already exists, this will - override all of the existing values that correspond to the key.

Parameters
key: string
The key to set values for.
values: Array
The values to set.
Returns
Decoded query string.
Returns
Encoded query string.

Instance Properties

The number of params, or null if it requires computing.

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. + ignoreCase_ is set to false, the keys will all be lower-cased.

Parameters
ignoreCase: boolean
whether this goog.Uri should ignore case.
code »setValues ( key, values )

Sets the values for a key. If the key already exists, this will + override all of the existing values that correspond to the key.

Parameters
key: string
The key to set values for.
values: Array
The values to set.
Returns
Decoded query string.
Returns
Encoded query string.

Instance Properties

The number of params, or null if it requires computing.

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.

Static Functions

code »goog.Uri.QueryData.createFromKeysValues ( keys, values, opt_uri, opt_ignoreCase )!goog.Uri.QueryData

Creates a new query data instance from parallel arrays of parameter names + not be problematic for IE.

Static Functions

code »goog.Uri.QueryData.createFromKeysValues ( keys, values, opt_uri, opt_ignoreCase )!goog.Uri.QueryData

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.

Parameters
keys: Array.<string>
Parameter names.
values: Array
Parameter values.
opt_uri: goog.Uri=
URI object that should have its cache invalidated when this object updates.
opt_ignoreCase: boolean=
If true, ignore the case of the parameter - name in #get.
Returns
The populated query data instance.
code »goog.Uri.QueryData.createFromMap ( map, opt_uri, opt_ignoreCase )!goog.Uri.QueryData

Creates a new query data instance from a map of names and values.

Parameters
map: (!goog.structs.Map|!Object)
Map of string parameter + name in #get.
Returns
The populated query data instance.
code »goog.Uri.QueryData.createFromMap ( map, opt_uri, opt_ignoreCase )!goog.Uri.QueryData

Creates a new query data instance from a map of names and values.

Parameters
map: (!goog.structs.Map|!Object)
Map of string parameter names to parameter value. If parameter value is an array, it is treated as if the key maps to each individual value in the array.
opt_uri: goog.Uri=
URI object that should have its cache invalidated when this object updates.
opt_ignoreCase: boolean=
If true, ignore the case of the parameter - name in #get.
Returns
The populated query data instance.
\ No newline at end of file + name in #get.Returns
The populated query data instance.
\ No newline at end of file diff --git a/docs/class_goog_asserts_AssertionError.html b/docs/class_goog_asserts_AssertionError.html index 5ad5eab..9b81597 100644 --- a/docs/class_goog_asserts_AssertionError.html +++ b/docs/class_goog_asserts_AssertionError.html @@ -1,4 +1,4 @@ -goog.asserts.AssertionError

Class goog.asserts.AssertionError

code »
Error
+goog.asserts.AssertionError

Class goog.asserts.AssertionError

code »
Errorgoog.debug.Error
-      └ goog.asserts.AssertionError

Error object for failed assertions.

Constructor

goog.asserts.AssertionError ( messagePattern, messageArgs )
Parameters
messagePattern: string
The pattern that was used to form message.
messageArgs: !Array
The items to substitute into the pattern.
Show:

Instance Properties

Defined in goog.asserts.AssertionError

The message pattern used to format the error message. Error handlers can - use this to uniquely identify the assertion.

Static Properties

\ No newline at end of file + └ goog.asserts.AssertionError

Error object for failed assertions.

Constructor

goog.asserts.AssertionError ( messagePattern, messageArgs )
Parameters
messagePattern: string
The pattern that was used to form message.
messageArgs: !Array
The items to substitute into the pattern.
Show:

Instance Properties

Defined in goog.asserts.AssertionError

The message pattern used to format the error message. Error handlers can + use this to uniquely identify the assertion.

Static Properties

\ No newline at end of file diff --git a/docs/class_goog_async_run_WorkItem_.html b/docs/class_goog_async_run_WorkItem_.html new file mode 100644 index 0000000..4cb9c4d --- /dev/null +++ b/docs/class_goog_async_run_WorkItem_.html @@ -0,0 +1 @@ +goog.async.run.WorkItem_

Class goog.async.run.WorkItem_

code »

Constructor

goog.async.run.WorkItem_ ( fn, scope )
Parameters
fn
scope
Show:

Instance Methods

Instance Properties

code »scope : (Object|null|undefined)
\ No newline at end of file diff --git a/docs/class_goog_debug_Error.html b/docs/class_goog_debug_Error.html index 15eb56f..0cf370c 100644 --- a/docs/class_goog_debug_Error.html +++ b/docs/class_goog_debug_Error.html @@ -1,2 +1,2 @@ goog.debug.Error

Class goog.debug.Error

code »
Error
-  └ goog.debug.Error

Base class for custom error objects.

Constructor

goog.debug.Error ( opt_msg )
Parameters
opt_msg: *=
The message associated with the error.
Show:

Static Properties

\ No newline at end of file + └ goog.debug.Error

Base class for custom error objects.

Constructor

goog.debug.Error ( opt_msg )
Parameters
opt_msg: *=
The message associated with the error.
Show:

Static Properties

\ No newline at end of file diff --git a/docs/class_goog_debug_LogBuffer.html b/docs/class_goog_debug_LogBuffer.html new file mode 100644 index 0000000..51f289c --- /dev/null +++ b/docs/class_goog_debug_LogBuffer.html @@ -0,0 +1,2 @@ +goog.debug.LogBuffer

Class goog.debug.LogBuffer

code »

Creates the log buffer.

Constructor

goog.debug.LogBuffer ( )

Classes

goog.debug.LogBuffer.instance_
Creates the log buffer.
Show:

Instance Methods

code »addRecord ( level, msg, loggerName )!goog.debug.LogRecord

Adds a log record to the buffer, possibly overwriting the oldest record.

Parameters
level: goog.debug.Logger.Level
One of the level identifiers.
msg: string
The string message.
loggerName: string
The name of the source logger.
Returns
The log record.

Removes all buffered log records.

Calls the given function for each buffered log record, starting with the + oldest one.

Parameters
func: function(!goog.debug.LogRecord)
The function to call.

Instance Properties

The array to store the records.

The index of the most recently added record or -1 if there are no records.

Whether the buffer is at capacity.

Static Functions

A static method that always returns the same instance of LogBuffer.

Returns
The LogBuffer singleton instance.
Returns
Whether the log buffer is enabled.

Static Properties

Compiler Constants

\ No newline at end of file diff --git a/docs/class_goog_debug_LogRecord.html b/docs/class_goog_debug_LogRecord.html new file mode 100644 index 0000000..32c248e --- /dev/null +++ b/docs/class_goog_debug_LogRecord.html @@ -0,0 +1,12 @@ +goog.debug.LogRecord

Class goog.debug.LogRecord

code »

LogRecord objects are used to pass logging requests between + the logging framework and individual log Handlers.

Constructor

goog.debug.LogRecord ( level, msg, loggerName, opt_time, opt_sequenceNumber )
Parameters
level: goog.debug.Logger.Level
One of the level identifiers.
msg: string
The string message.
loggerName: string
The name of the source logger.
opt_time: number=
Time this log record was created if other than now. + If 0, we use #goog.now.
opt_sequenceNumber: number=
Sequence number of this log record. This + should only be passed in when restoring a log record from persistence.
Show:

Instance Methods

Get the exception that is part of the log record.

Returns
the exception.

Get the exception text that is part of the log record.

Returns
Exception text.

Get the logging message level, for example Level.SEVERE.

Returns
the logging message level.

Get the source Logger's name.

Returns
source logger name (may be null).

Get the "raw" log message, before localization or formatting.

Returns
the raw message string.

Get event time in milliseconds since 1970.

Returns
event time in millis since 1970.

Get the sequence number. +

+ Sequence numbers are normally assigned in the LogRecord + constructor, which assigns unique sequence numbers to + each new LogRecord in increasing order.

Returns
the sequence number.
code »reset ( level, msg, loggerName, opt_time, opt_sequenceNumber )

Sets all fields of the log record.

Parameters
level: goog.debug.Logger.Level
One of the level identifiers.
msg: string
The string message.
loggerName: string
The name of the source logger.
opt_time: number=
Time this log record was created if other than now. + If 0, we use #goog.now.
opt_sequenceNumber: number=
Sequence number of this log record. This + should only be passed in when restoring a log record from persistence.
code »setException ( exception )

Set the exception that is part of the log record.

Parameters
exception: Object
the exception.

Set the exception text that is part of the log record.

Parameters
text: string
The exception text.
code »setLevel ( level )

Set the logging message level, for example Level.SEVERE.

Parameters
level: goog.debug.Logger.Level
the logging message level.
code »setLoggerName ( loggerName )

Get the source Logger's name.

Parameters
loggerName: string
source logger name (may be null).

Set the "raw" log message, before localization or formatting.

Parameters
msg: string
the raw message string.

Set event time in milliseconds since 1970.

Parameters
time: number
event time in millis since 1970.

Instance Properties

Exception text associated with the record

Exception associated with the record

Level of the LogRecord

Name of the logger that created the record.

Message associated with the record

Sequence number for the LogRecord. Each record has a unique sequence number + that is greater than all log records created before it.

Time the LogRecord was created.

Static Properties

A sequence counter for assigning increasing sequence numbers to LogRecord + objects.

Compiler Constants

\ No newline at end of file diff --git a/docs/class_goog_debug_Logger.html b/docs/class_goog_debug_Logger.html new file mode 100644 index 0000000..6d70260 --- /dev/null +++ b/docs/class_goog_debug_Logger.html @@ -0,0 +1,59 @@ +goog.debug.Logger

Class goog.debug.Logger

code »

The Logger is an object used for logging debug messages. Loggers are + normally named, using a hierarchical dot-separated namespace. Logger names + can be arbitrary strings, but they should normally be based on the package + name or class name of the logged component, such as goog.net.BrowserChannel. + + The Logger object is loosely based on the java class + java.util.logging.Logger. It supports different levels of filtering for + different loggers. + + The logger object should never be instantiated by application code. It + should always use the goog.debug.Logger.getLogger function.

Constructor

goog.debug.Logger ( name )
Parameters
name: string
The name of the Logger.

Classes

goog.debug.Logger.Level
The Level class defines a set of standard logging levels that + can be used to control logging output.
Show:

Instance Methods

code »addChild_ ( name, logger )

Adds a child to this logger. This is used for setting up the logger tree.

Parameters
name: string
The leaf name of the child.
logger: goog.debug.Logger
The child logger.
code »addHandler ( handler )

Adds a handler to the logger. This doesn't use the event system because + we want to be able to add logging to the event system.

Parameters
handler: Function
Handler function to add.
code »callPublish_ ( logRecord )

Calls the handlers for publish.

Parameters
logRecord: goog.debug.LogRecord
The log record to publish.
code »config ( msg, opt_exception )

Logs a message at the Logger.Level.CONFIG level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.
code »doLogRecord_ ( logRecord )

Logs a LogRecord.

Parameters
logRecord: goog.debug.LogRecord
A log record to log.
code »fine ( msg, opt_exception )

Logs a message at the Logger.Level.FINE level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.
code »finer ( msg, opt_exception )

Logs a message at the Logger.Level.FINER level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.
code »finest ( msg, opt_exception )

Logs a message at the Logger.Level.FINEST level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.

Returns the children of this logger as a map of the child name to the logger.

Returns
The map where the keys are the child leaf names and the + values are the Logger objects.

Returns the effective level of the logger based on its ancestors' levels.

Returns
The level.

Gets the log level specifying which message levels will be logged by this + logger. Message levels lower than this value will be discarded. + The level value Level.OFF can be used to turn off logging. If the level + is null, it means that this node should inherit its level from its nearest + ancestor with a specific (non-null) level value.

Returns
The level.
code »getLogRecord ( level, msg, opt_exception, opt_fnStackContext )!goog.debug.LogRecord

Creates a new log record and adds the exception (if present) to it.

Parameters
level: goog.debug.Logger.Level
One of the level identifiers.
msg: string
The string message.
opt_exception: (Error|Object)=
An exception associated with the + message.
opt_fnStackContext: Function=
A function to use as the base + of the stack trace used in the log record.
Returns
A log record.

Gets the name of this logger.

Returns
The name of this logger.

Returns the parent of this logger.

Returns
The parent logger or null if this is the root.
code »info ( msg, opt_exception )

Logs a message at the Logger.Level.INFO level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.

Checks if a message of the given level would actually be logged by this + logger. This check is based on the Loggers effective level, which may be + inherited from its parent.

Parameters
level: goog.debug.Logger.Level
The level to check.
Returns
Whether the message would be logged.
code »log ( level, msg, opt_exception )

Logs a message. If the logger is currently enabled for the + given message level then the given message is forwarded to all the + registered output Handler objects.

Parameters
level: goog.debug.Logger.Level
One of the level identifiers.
msg: goog.debug.Loggable
The message to log.
opt_exception: (Error|Object)=
An exception associated with the + message.
code »logRecord ( logRecord )

Logs a LogRecord. If the logger is currently enabled for the + given message level then the given message is forwarded to all the + registered output Handler objects.

Parameters
logRecord: goog.debug.LogRecord
A log record to log.

Removes a handler from the logger. This doesn't use the event system because + we want to be able to add logging to the event system.

Parameters
handler: Function
Handler function to remove.
Returns
Whether the handler was removed.
code »setLevel ( level )

Set the log level specifying which message levels will be logged by this + logger. Message levels lower than this value will be discarded. + The level value Level.OFF can be used to turn off logging. If the new level + is null, it means that this node should inherit its level from its nearest + ancestor with a specific (non-null) level value.

Parameters
level: goog.debug.Logger.Level
The new level.

Sets the parent of this logger. This is used for setting up the logger tree.

Parameters
parent: goog.debug.Logger
The parent logger.
code »severe ( msg, opt_exception )

Logs a message at the Logger.Level.SEVERE level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.
code »shout ( msg, opt_exception )

Logs a message at the Logger.Level.SHOUT level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.
code »warning ( msg, opt_exception )

Logs a message at the Logger.Level.WARNING level. + If the logger is currently enabled for the given message level then the + given message is forwarded to all the registered output Handler objects.

Parameters
msg: goog.debug.Loggable
The message to log.
opt_exception: Error=
An exception associated with the message.

Instance Properties

Map of children loggers. The keys are the leaf names of the children and + the values are the child loggers.

Handlers that are listening to this logger.

Level that this logger only filters above. Null indicates it should + inherit from the parent.

Name of the Logger. Generally a dot-separated namespace

Static Functions

Deprecated: use goog.log instead. http://go/goog-debug-logger-deprecated

Finds or creates a logger for a named subsystem. If a logger has already been + created with the given name it is returned. Otherwise a new logger is + created. If a new logger is created its log level will be configured based + on the LogManager configuration and it will configured to also send logging + output to its parent's handlers. It will be registered in the LogManager + global namespace.

Parameters
name: string
A name for the logger. This should be a dot-separated + name and should normally be based on the package name or class name of the + subsystem, such as goog.net.BrowserChannel.
Returns
The named logger.

Logs a message to profiling tools, if available. + https://developers.google.com/web-toolkit/speedtracer/logging-api + http://msdn.microsoft.com/en-us/library/dd433074(VS.85).aspx

Parameters
msg: string
The message to log.

Static Properties

Compiler Constants

\ No newline at end of file diff --git a/docs/class_goog_debug_Logger_Level.html b/docs/class_goog_debug_Logger_Level.html new file mode 100644 index 0000000..3454ffd --- /dev/null +++ b/docs/class_goog_debug_Logger_Level.html @@ -0,0 +1,32 @@ +goog.debug.Logger.Level

Class goog.debug.Logger.Level

code »

The Level class defines a set of standard logging levels that + can be used to control logging output. The logging Level objects + are ordered and are specified by ordered integers. Enabling logging + at a given level also enables logging at all higher levels. +

+ Clients should normally use the predefined Level constants such + as Level.SEVERE. +

+ The levels in descending order are: +

    +
  • SEVERE (highest value) +
  • WARNING +
  • INFO +
  • CONFIG +
  • FINE +
  • FINER +
  • FINEST (lowest value) +
+ In addition there is a level OFF that can be used to turn + off logging, and a level ALL that can be used to enable + logging of all messages.

Constructor

goog.debug.Logger.Level ( name, value )
Parameters
name: string
The name of the level.
value: number
The numeric value of the level.
Show:

Instance Methods

Returns
String representation of the logger level.

Instance Properties

The name of the level

The numeric value of the level

Static Functions

Creates the predefined levels cache and populates it.

Gets the predefined level with the given name.

Parameters
name: string
The name of the level.
Returns
The level, or null if none found.

Gets the highest predefined level <= #value.

Parameters
value: number
Level value.
Returns
The level, or null if none found.

Static Properties

ALL indicates that all messages should be logged. + This level is initialized to 0.

CONFIG is a message level for static configuration messages. + This level is initialized to 700.

FINE is a message level providing tracing information. + This level is initialized to 500.

FINER indicates a fairly detailed tracing message. + This level is initialized to 400.

FINEST indicates a highly detailed tracing message. + This level is initialized to 300.

INFO is a message level for informational messages. + This level is initialized to 800.

OFF is a special level that can be used to turn off logging. + This level is initialized to Infinity.

SEVERE is a message level indicating a serious failure. + This level is initialized to 1000.

SHOUT is a message level for extra debugging loudness. + This level is initialized to 1200.

WARNING is a message level indicating a potential problem. + This level is initialized to 900.

A lookup map used to find the level object based on the name or value of + the level object.

\ No newline at end of file diff --git a/docs/class_goog_dom_DomHelper.html b/docs/class_goog_dom_DomHelper.html new file mode 100644 index 0000000..caa6f2e --- /dev/null +++ b/docs/class_goog_dom_DomHelper.html @@ -0,0 +1,104 @@ +goog.dom.DomHelper

Class goog.dom.DomHelper

code »

Create an instance of a DOM helper with a new document object.

Constructor

goog.dom.DomHelper ( opt_document )
Parameters
opt_document: Document=
Document object to associate with this + DOM helper.
Show:

Instance Methods

code »$ ( element )Element

Alias for getElement.

Parameters
element: (string|Element)
Element ID or a DOM node.
Returns
The element with the given ID, or the node passed in.
code »$$ ( opt_tag, opt_class, opt_el ){length: number}
Deprecated: Use DomHelper getElementsByTagNameAndClass.

Alias for getElementsByTagNameAndClass.

Parameters
opt_tag: ?string=
Element tag name.
opt_class: ?string=
Optional class name.
opt_el: Element=
Optional element to look in.
Returns
Array-like list of elements (only a length + property and numerical indices are guaranteed to exist).
code »$dom ( tagName, opt_attributes, var_args )!Element

Alias for createDom.

Parameters
tagName: string
Tag to create.
opt_attributes: (Object|string)=
If object, then a map of name-value + pairs for attributes. If a string, then this is the className of the new + element.
var_args: ...goog.dom.Appendable
Further DOM nodes or strings for + text nodes. If one of the var_args is an array, its children will be + added as childNodes instead.
Returns
Reference to a DOM node.
code »append ( parent, var_args )

Appends a node with text or other nodes.

Parameters
parent: !Node
The node to append nodes to.
var_args: ...goog.dom.Appendable
The things to append to the node. + If this is a Node it is appended as is. + If this is a string then a text node is appended. + If this is an array like object then fields 0 to length - 1 are appended.
code »appendChild ( parent, child )

Appends a child to a node.

Parameters
parent: Node
Parent.
child: Node
Child.

Determines if the given node can contain children, intended to be used for + HTML generation.

Parameters
node: Node
The node to check.
Returns
Whether the node can contain children.
code »compareNodeOrder ( node1, node2 )number

Compares the document order of two nodes, returning 0 if they are the same + node, a negative number if node1 is before node2, and a positive number if + node2 is before node1. Note that we compare the order the tags appear in the + document so in the tree text the B node is considered to be + before the I node.

Parameters
node1: Node
The first node to compare.
node2: Node
The second node to compare.
Returns
0 if the nodes are the same node, a negative number if node1 + is before node2, and a positive number if node2 is before node1.
code »contains ( parent, descendant )boolean

Whether a node contains another node.

Parameters
parent: Node
The node that should contain the other node.
descendant: Node
The node to test presence of.
Returns
Whether the parent node contains the descendent node.
code »createDom ( tagName, opt_attributes, var_args )!Element

Returns a dom node with a set of attributes. This function accepts varargs + for subsequent nodes to be added. Subsequent nodes will be added to the + first node as childNodes. + + So: + createDom('div', null, createDom('p'), createDom('p')); + would return a div with two child paragraphs + + An easy way to move all child nodes of an existing element to a new parent + element is: + createDom('div', null, oldElement.childNodes); + which will remove all child nodes from the old element and add them as + child nodes of the new DIV.

Parameters
tagName: string
Tag to create.
opt_attributes: (Object|string)=
If object, then a map of name-value + pairs for attributes. If a string, then this is the className of the new + element.
var_args: ...goog.dom.Appendable
Further DOM nodes or + strings for text nodes. If one of the var_args is an array or + NodeList, its elements will be added as childNodes instead.
Returns
Reference to a DOM node.

Creates a new element.

Parameters
name: string
Tag name.
Returns
The new element.
code »createTable ( rows, columns, opt_fillWithNbsp )!Element

Create a table.

Parameters
rows: number
The number of rows in the table. Must be >= 1.
columns: number
The number of columns in the table. Must be >= 1.
opt_fillWithNbsp: boolean=
If true, fills table entries with nsbps.
Returns
The created table.
code »createTextNode ( content )!Text

Creates a new text node.

Parameters
content: (number|string)
Content.
Returns
The new text node.

Find the deepest common ancestor of the given nodes.

Parameters
var_args: ...Node
The nodes to find a common ancestor of.
Returns
The common ancestor of the nodes, or null if there is none. + null will only be returned if two or more of the nodes are from different + documents.
code »findNode ( root, p )(Node|undefined)

Finds the first descendant node that matches the filter function. This does + a depth first search.

Parameters
root: Node
The root of the tree to search.
p: function(Node): boolean
The filter function.
Returns
The found node or undefined if none is found.
code »findNodes ( root, p )Array.<Node>

Finds all the descendant nodes that matches the filter function. This does a + depth first search.

Parameters
root: Node
The root of the tree to search.
p: function(Node): boolean
The filter function.
Returns
The found nodes or an empty array if none are found.

Flattens an element. That is, removes it and replace it with its children.

Parameters
element: Element
The element to flatten.
Returns
The original element, detached from the document + tree, sans children, or undefined if the element was already not in the + document.

Determines the active element in the given document.

Parameters
opt_doc: Document=
The document to look in.
Returns
The active element.
code »getAncestor ( element, matcher, opt_includeNode, opt_maxSearchSteps )Node

Walks up the DOM hierarchy returning the first ancestor that passes the + matcher function.

Parameters
element: Node
The DOM node to start with.
matcher: function(Node): boolean
A function that returns true if the + passed node matches the desired criteria.
opt_includeNode: boolean=
If true, the node itself is included in + the search (the first call to the matcher will pass startElement as + the node to test).
opt_maxSearchSteps: number=
Maximum number of levels to search up the + dom.
Returns
DOM node that matched the matcher, or null if there was + no match.
code »getAncestorByClass ( element, class )Element

Walks up the DOM hierarchy returning the first ancestor that has the passed + class name. If the passed element matches the specified criteria, the + element itself is returned.

Parameters
element: Node
The DOM node to start with.
class: string
The class name to match.
Returns
The first ancestor that matches the passed criteria, or + null if none match.
code »getAncestorByTagNameAndClass ( element, opt_tag, opt_class )Element

Walks up the DOM hierarchy returning the first ancestor that has the passed + tag name and/or class name. If the passed element matches the specified + criteria, the element itself is returned.

Parameters
element: Node
The DOM node to start with.
opt_tag: ?(goog.dom.TagName|string)=
The tag name to match (or + null/undefined to match only based on class name).
opt_class: ?string=
The class name to match (or null/undefined to + match only based on tag name).
Returns
The first ancestor that matches the passed criteria, or + null if no match is found.
code »getChildren ( element )!(Array|NodeList)

Returns an array containing just the element children of the given element.

Parameters
element: Element
The element whose element children we want.
Returns
An array or array-like list of just the element + children of the given element.

Gets the document object being used by the dom library.

Returns
Document object.

Calculates the height of the document.

Returns
The height of the document.

Gets the document scroll distance as a coordinate object.

Returns
Object with properties 'x' and 'y'.

Gets the document scroll element.

Returns
Scrolling element.

Gets the dom helper object for the document where the element resides.

Parameters
opt_node: Node=
If present, gets the DomHelper for this node.
Returns
The DomHelper.
code »getElement ( element )Element

Alias for getElementById. If a DOM node is passed in then we just + return that.

Parameters
element: (string|Element)
Element ID or a DOM node.
Returns
The element with the given ID, or the node passed in.
code »getElementByClass ( className, opt_el )Element

Returns the first element we find matching the provided class name.

Parameters
className: string
the name of the class to look for.
opt_el: (Element|Document)=
Optional element to look in.
Returns
The first item found with the class name provided.
code »getElementsByClass ( className, opt_el ){length: number}

Returns an array of all the elements with the provided className.

Parameters
className: string
the name of the class to look for.
opt_el: (Element|Document)=
Optional element to look in.
Returns
The items found with the class name provided.
code »getElementsByTagNameAndClass ( opt_tag, opt_class, opt_el ){length: number}

Looks up elements by both tag and class name, using browser native functions + (querySelectorAll, getElementsByTagName or + getElementsByClassName) where possible. The returned array is a live + NodeList or a static list depending on the code path taken.

Parameters
opt_tag: ?string=
Element tag name or * for all tags.
opt_class: ?string=
Optional class name.
opt_el: (Document|Element)=
Optional element to look in.
Returns
Array-like list of elements (only a length + property and numerical indices are guaranteed to exist).

Returns the first child node that is an element.

Parameters
node: Node
The node to get the first child element of.
Returns
The first child node of node that is an element.

Cross browser function for getting the document element of an iframe.

Parameters
iframe: Element
Iframe element.
Returns
The frame content document.
code »getFrameContentWindow ( frame )Window

Cross browser function for getting the window of a frame or iframe.

Parameters
frame: Element
Frame element.
Returns
The window associated with the given frame.

Returns the last child node that is an element.

Parameters
node: Node
The node to get the last child element of.
Returns
The last child node of node that is an element.

Returns the first next sibling that is an element.

Parameters
node: Node
The node to get the next sibling element of.
Returns
The next sibling of node that is an element.

Returns the next node in source order from the given node.

Parameters
node: Node
The node.
Returns
The next node in the DOM tree, or null if this was the last + node.
code »getNodeAtOffset ( parent, offset, opt_result )Node

Returns the node at a given offset in a parent node. If an object is + provided for the optional third parameter, the node and the remainder of the + offset will stored as properties of this object.

Parameters
parent: Node
The parent node.
offset: number
The offset into the parent node.
opt_result: Object=
Object to be used to store the return value. The + return value will be stored in the form {node: Node, remainder: number} + if this object is provided.
Returns
The node at the given offset.

Returns the text length of the text contained in a node, without markup. This + is equivalent to the selection length if the node was selected, or the number + of cursor movements to traverse the node. Images & BRs take one space. New + lines are ignored.

Parameters
node: Node
The node whose text content length is being calculated.
Returns
The length of node's text content.
code »getNodeTextOffset ( node, opt_offsetParent )number

Returns the text offset of a node relative to one of its ancestors. The text + length is the same as the length calculated by + goog.dom.getNodeTextLength.

Parameters
node: Node
The node whose offset is being calculated.
opt_offsetParent: Node=
Defaults to the node's owner document's body.
Returns
The text offset.
code »getOuterHtml ( element )string

Gets the outerHTML of a node, which islike innerHTML, except that it + actually contains the HTML of the node itself.

Parameters
element: Element
The element to get the HTML of.
Returns
The outerHTML of the given element.

Returns the owner document for a node.

Parameters
node: Node
The node to get the document for.
Returns
The document owning the node.

Returns an element's parent, if it's an Element.

Parameters
element: Element
The DOM element.
Returns
The parent, or null if not an Element.

Returns the first previous sibling that is an element.

Parameters
node: Node
The node to get the previous sibling element of.
Returns
The first previous sibling of node that is + an element.

Returns the previous node in source order from the given node.

Parameters
node: Node
The node.
Returns
The previous node in the DOM tree, or null if this was the + first node.

Gets an element by id, asserting that the element is found. + + This is used when an element is expected to exist, and should fail with + an assertion error if it does not (if assertions are enabled).

Parameters
id: string
Element ID.
Returns
The element with the given ID, if it exists.
code »getRequiredElementByClass ( className, opt_root )!Element

Ensures an element with the given className exists, and then returns the + first element with the provided className.

Parameters
className: string
the name of the class to look for.
opt_root: (!Element|!Document)=
Optional element or document to look + in.
Returns
The first item found with the class name provided.
Throws
goog.asserts.AssertionError
Thrown if no element is found.

Returns the text contents of the current node, without markup. New lines are + stripped and whitespace is collapsed, such that each character would be + visible. + + In browsers that support it, innerText is used. Other browsers attempt to + simulate it via node traversal. Line breaks are canonicalized in IE.

Parameters
node: Node
The node from which we are getting content.
Returns
The text content.

Gets the dimensions of the viewport.

Parameters
opt_window: Window=
Optional window element to test. Defaults to + the window of the Dom Helper.
Returns
Object with values 'width' and 'height'.
code »getWindow ( )!Window

Gets the window object associated with the document.

Returns
The window associated with the given document.

Converts an HTML string into a node or a document fragment. A single Node + is used if the htmlString only generates a single node. If the + htmlString generates multiple nodes then these are put inside a + DocumentFragment.

Parameters
htmlString: string
The HTML string to convert.
Returns
The resulting node.
code »insertChildAt ( parent, child, index )

Insert a child at a given index. If index is larger than the number of child + nodes that the parent currently has, the node is inserted as the last child + node.

Parameters
parent: Element
The element into which to insert the child.
child: Node
The element to insert.
index: number
The index at which to insert the new child node. Must + not be negative.
code »insertSiblingAfter ( newNode, refNode )

Inserts a new node after an existing reference node (i.e., as the next + sibling). If the reference node has no parent, then does nothing.

Parameters
newNode: Node
Node to insert.
refNode: Node
Reference node to insert after.
code »insertSiblingBefore ( newNode, refNode )

Inserts a new node before an existing reference node (i.e., as the previous + sibling). If the reference node has no parent, then does nothing.

Parameters
newNode: Node
Node to insert.
refNode: Node
Reference node to insert before.

Returns true if the browser is in "CSS1-compatible" (standards-compliant) + mode, false otherwise.

Returns
True if in CSS1-compatible mode.

Whether the object looks like an Element.

Parameters
obj: ?
The object being tested for Element likeness.
Returns
Whether the object looks like an Element.
code »isFocusable ( element )boolean

Returns true if the element can be focused, i.e. it has a tab index that + allows it to receive keyboard focus (tabIndex >= 0), or it is an element + that natively supports keyboard focus.

Parameters
element: Element
Element to check.
Returns
Whether the element allows keyboard focus.

Returns true if the element has a tab index that allows it to receive + keyboard focus (tabIndex >= 0), false otherwise. Note that some elements + natively support keyboard focus, even if they have no tab index.

Parameters
element: Element
Element to check.
Returns
Whether the element has a tab index that allows keyboard + focus.

Whether the object looks like a DOM node.

Parameters
obj: ?
The object being tested for node likeness.
Returns
Whether the object looks like a DOM node.

Returns true if the object is a NodeList. To qualify as a NodeList, + the object must have a numeric length property and an item function (which + has type 'string' on IE for some reason).

Parameters
val: Object
Object to test.
Returns
Whether the object is a NodeList.

Returns true if the specified value is a Window object. This includes the + global window for HTML pages, and iframe windows.

Parameters
obj: ?
Variable to test.
Returns
Whether the variable is a window.

Removes all the child nodes on a DOM node.

Parameters
node: Node
Node to remove children from.
code »removeNode ( node )Node

Removes a node from its parent.

Parameters
node: Node
The node to remove.
Returns
The node removed if removed; else, null.
code »replaceNode ( newNode, oldNode )

Replaces a node in the DOM tree. Will do nothing if oldNode has no + parent.

Parameters
newNode: Node
Node to insert.
oldNode: Node
Node to replace.
code »setDocument ( document )

Sets the document object.

Parameters
document: !Document
Document object.
code »setFocusableTabIndex ( element, enable )

Enables or disables keyboard focus support on the element via its tab index. + Only elements for which goog.dom.isFocusableTabIndex returns true + (or elements that natively support keyboard focus, like form elements) can + receive keyboard focus. See http://go/tabindex for more info.

Parameters
element: Element
Element whose tab index is to be changed.
enable: boolean
Whether to set or remove a tab index on the element + that supports keyboard focus.
code »setProperties ( element, properties )

Sets a number of properties on a node.

Parameters
element: Element
DOM node to set properties on.
properties: Object
Hash of property:value pairs.
code »setTextContent ( node, text )

Sets the text content of a node, with cross-browser support.

Parameters
node: Node
The node to change the text content of.
text: (string|number)
The value that should replace the node's content.

Instance Properties

Reference to the document object to use

\ No newline at end of file diff --git a/docs/class_goog_events_BrowserEvent.html b/docs/class_goog_events_BrowserEvent.html new file mode 100644 index 0000000..ed9fdc9 --- /dev/null +++ b/docs/class_goog_events_BrowserEvent.html @@ -0,0 +1,25 @@ +goog.events.BrowserEvent

Class goog.events.BrowserEvent

code »
goog.events.Event
+  └ goog.events.BrowserEvent

Accepts a browser event object and creates a patched, cross browser event + object. + The content of this object will not be initialized if no event object is + provided. If this is the case, init() needs to be invoked separately.

Constructor

goog.events.BrowserEvent ( opt_e, opt_currentTarget )
Parameters
opt_e: Event=
Browser event object.
opt_currentTarget: EventTarget=
Current target for event.

Enumerations

goog.events.BrowserEvent.MouseButton
Normalized button constants for the mouse.
Show:

Instance Methods

Defined in goog.events.BrowserEvent

Returns
The underlying browser event object.
code »init ( e, opt_currentTarget )

Accepts a browser event object and creates a patched, cross browser event + object.

Parameters
e: Event
Browser event object.
opt_currentTarget: EventTarget=
Current target for event.
code »isButton ( button )boolean

Tests to see which button was pressed during the event. This is really only + useful in IE and Gecko browsers. And in IE, it's only useful for + mousedown/mouseup events, because click only fires for the left mouse button. + + Safari 2 only reports the left button being clicked, and uses the value '1' + instead of 0. Opera only reports a mousedown event for the middle button, and + no mouse events for the right button. Opera has default behavior for left and + middle click that can only be overridden via a configuration setting. + + There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.

Parameters
button: goog.events.BrowserEvent.MouseButton
The button + to test for.
Returns
True if button was pressed.

Whether this has an "action"-producing mouse button. + + By definition, this includes left-click on windows/linux, and left-click + without the ctrl key on Macs.

Returns
The result.

Defined in goog.events.Event

Deprecated: Events don't need to be disposed.

For backwards compatibility (goog.events.Event used to inherit + goog.Disposable).

Instance Properties

Defined in goog.events.BrowserEvent

Whether alt was pressed at time of event.

Which mouse button was pressed.

Keycode of key press.

X-coordinate relative to the window.

Y-coordinate relative to the window.

Whether control was pressed at time of event.

Node that had the listener attached.

The browser event object.

Keycode of key press.

Whether the meta key was pressed at time of event.

X-coordinate relative to target.

Y-coordinate relative to target.

Whether the default platform modifier key was pressed at time of event. + (This is control for all platforms except Mac, where it's Meta.)

For mouseover and mouseout events, the related object for the event.

X-coordinate relative to the monitor.

Y-coordinate relative to the monitor.

Whether shift was pressed at time of event.

History state object, only set for PopState events where it's a copy of the + state object provided to pushState or replaceState.

Target that fired the event.

Defined in goog.events.Event

Whether the default action has been prevented. + This is a property to match the W3C specification at + #events-event-type-defaultPrevented. + Must be treated as read-only outside the class.

Whether to cancel the event in internal capture/bubble processing for IE.

Return value for in internal capture/bubble processing for IE.

Event type.

Static Properties

Static data for mapping mouse buttons.

\ No newline at end of file diff --git a/docs/class_goog_events_Event.html b/docs/class_goog_events_Event.html new file mode 100644 index 0000000..654a97d --- /dev/null +++ b/docs/class_goog_events_Event.html @@ -0,0 +1,16 @@ +goog.events.Event

Class goog.events.Event

code »

A base class for event objects, so that they can support preventDefault and + stopPropagation.

Constructor

goog.events.Event ( type, opt_target )
Parameters
type: (string|!goog.events.EventId)
Event Type.
opt_target: Object=
Reference to the object that is the target of + this event. It has to implement the EventTarget interface + declared at http://developer.mozilla.org/en/DOM/EventTarget.
Show:

Instance Methods

Deprecated: Events don't need to be disposed.

For backwards compatibility (goog.events.Event used to inherit + goog.Disposable).

Deprecated: Events don't need to be disposed.

For backwards compatibility (goog.events.Event used to inherit + goog.Disposable).

Prevents the default action, for example a link redirecting to a url.

Stops event propagation.

Instance Properties

Object that had the listener attached.

Whether the default action has been prevented. + This is a property to match the W3C specification at + #events-event-type-defaultPrevented. + Must be treated as read-only outside the class.

Whether to cancel the event in internal capture/bubble processing for IE.

Return value for in internal capture/bubble processing for IE.

TODO(user): The type should probably be + EventTarget|goog.events.EventTarget. + + Target of the event.

Event type.

Static Functions

Prevents the default action. It is equivalent to + e.preventDefault(), but can be used as the callback argument of + goog.events.listen without declaring another function.

Parameters
e: !goog.events.Event
An event.

Stops the propagation of the event. It is equivalent to + e.stopPropagation(), but can be used as the callback argument of + goog.events.listen without declaring another function.

Parameters
e: !goog.events.Event
An event.
\ No newline at end of file diff --git a/docs/class_goog_events_EventId.html b/docs/class_goog_events_EventId.html new file mode 100644 index 0000000..4511526 --- /dev/null +++ b/docs/class_goog_events_EventId.html @@ -0,0 +1,10 @@ +goog.events.EventId

Class goog.events.EventId.<T>

code »

A templated class that is used when registering for events. Typical usage: + + /** @type {goog.events.EventId.} + var myEventId = new goog.events.EventId( + goog.events.getUniqueId(('someEvent')); + + // No need to cast or declare here since the compiler knows the correct + // type of 'evt' (MyEventObj). + something.listen(myEventId, function(evt) {}); +

Constructor

goog.events.EventId ( eventId )
Parameters
eventId
Show:

Instance Methods

code »toString ( )string

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_events_EventTarget.html b/docs/class_goog_events_EventTarget.html new file mode 100644 index 0000000..2cc42c8 --- /dev/null +++ b/docs/class_goog_events_EventTarget.html @@ -0,0 +1,68 @@ +goog.events.EventTarget

Class goog.events.EventTarget

code »
goog.Disposable
+  └ goog.events.EventTarget
All implemented interfaces:
goog.disposable.IDisposable, goog.events.Listenable

An implementation of goog.events.Listenable with full W3C + EventTarget-like support (capture/bubble mechanism, stopping event + propagation, preventing default actions). + + You may subclass this class to turn your class into a Listenable. + + Unless propagation is stopped, an event dispatched by an + EventTarget will bubble to the parent returned by + getParentEventTarget. To set the parent, call + setParentEventTarget. Subclasses that don't support + changing the parent can override the setter to throw an error. + + Example usage: +

+   var source = new goog.events.EventTarget();
+   function handleEvent(e) {
+     alert('Type: ' + e.type + '; Target: ' + e.target);
+   }
+   source.listen('foo', handleEvent);
+   // Or: goog.events.listen(source, 'foo', handleEvent);
+   ...
+   source.dispatchEvent('foo');  // will call handleEvent
+   ...
+   source.unlisten('foo', handleEvent);
+   // Or: goog.events.unlisten(source, 'foo', handleEvent);
+ 

Constructor

goog.events.EventTarget ( )
Show:

Instance Methods

Defined in goog.events.EventTarget

code »addEventListener ( type, handler, opt_capture, opt_handlerScope )
Deprecated: Use #listen instead, when possible. Otherwise, use + goog.events.listen if you are passing Object + (instead of Function) as handler.

Adds an event listener to the event target. The same handler can only be + added once per the type. Even if you add the same handler multiple times + using the same type then it will only be called once when the event is + dispatched.

Parameters
type: string
The type of the event to listen for.
handler: (function(?): ?|{handleEvent: function(?): ?}|null)
The function + to handle the event. The handler can also be an object that implements + the handleEvent method which takes the event object as argument.
opt_capture: boolean=
In DOM-compliant browsers, this determines + whether the listener is fired during the capture or bubble phase + of the event.
opt_handlerScope: Object=
Object in whose scope to call + the listener.

Asserts that the event target instance is initialized properly.

code »dispatchEvent ( e )boolean
Parameters
e

Removes listeners from this object. Classes that extend EventTarget may + need to override this method in order to remove references to DOM Elements + and additional listeners.

code »fireListeners ( type, capture, eventObject )boolean
Parameters
type
capture
eventObject
code »getListener ( type, listener, capture, opt_listenerScope )(goog.events.ListenableKey|null)
Parameters
type
listener
capture
opt_listenerScope
code »getListeners ( type, capture )Array.<(goog.events.ListenableKey|null)>
Parameters
type
capture

Returns the parent of this event target to use for bubbling.

Returns
The parent EventTarget or null if + there is no parent.
code »hasListener ( opt_type, opt_capture )boolean
Parameters
opt_type
opt_capture
code »listen ( type, listener, opt_useCapture, opt_listenerScope )(goog.events.ListenableKey|null)
Parameters
type
listener
opt_useCapture
opt_listenerScope
code »listenOnce ( type, listener, opt_useCapture, opt_listenerScope )(goog.events.ListenableKey|null)
Parameters
type
listener
opt_useCapture
opt_listenerScope
code »removeAllListeners ( opt_type )number
Parameters
opt_type
code »removeEventListener ( type, handler, opt_capture, opt_handlerScope )
Deprecated: Use #unlisten instead, when possible. Otherwise, use + goog.events.unlisten if you are passing Object + (instead of Function) as handler.

Removes an event listener from the event target. The handler must be the + same object as the one added. If the handler has not been added then + nothing is done.

Parameters
type: string
The type of the event to listen for.
handler: (function(?): ?|{handleEvent: function(?): ?}|null)
The function + to handle the event. The handler can also be an object that implements + the handleEvent method which takes the event object as argument.
opt_capture: boolean=
In DOM-compliant browsers, this determines + whether the listener is fired during the capture or bubble phase + of the event.
opt_handlerScope: Object=
Object in whose scope to call + the listener.

Sets the parent of this event target to use for capture/bubble + mechanism.

Parameters
parent: goog.events.EventTarget
Parent listenable (null if none).

Sets the target to be used for event.target when firing + event. Mainly used for testing. For example, see + goog.testing.events.mixinListenable.

Parameters
target: !Object
The target.
code »unlisten ( type, listener, opt_useCapture, opt_listenerScope )boolean
Parameters
type
listener
opt_useCapture
opt_listenerScope
code »unlistenByKey ( key )boolean
Parameters
key

Defined in goog.Disposable

code »<T> addOnDisposeCallback ( callback, opt_scope )

Invokes a callback function when this object is disposed. Callbacks are + invoked in the order in which they were added.

Parameters
callback: function(this: T): ?
The callback function.
opt_scope: T=
An optional scope to call the callback in.
code »dispose ( )void

Disposes of the object. If the object hasn't already been disposed of, calls + #disposeInternal. Classes that extend goog.Disposable should + override #disposeInternal in order to delete references to COM + objects, DOM nodes, and other disposable objects. Reentrant.

Returns
Nothing.
Deprecated: Use #isDisposed instead.
Returns
Whether the object has been disposed of.
Returns
Whether the object has been disposed of.

Associates a disposable object with this object so that they will be disposed + together.

Parameters
disposable: goog.disposable.IDisposable
that will be disposed when + this object is disposed.

Instance Properties

Defined in goog.events.EventTarget

The object to use for event.target. Useful when mixing in an + EventTarget to another object.

Maps of event type to an array of listeners.

Parent event target, used during event bubbling. + + TODO(user): Change this to goog.events.Listenable. This + currently breaks people who expect getParentEventTarget to return + goog.events.EventTarget.

Defined in goog.Disposable

If monitoring the goog.Disposable instances is enabled, stores the creation + stack trace of the Disposable instance.

Whether the object has been disposed of.

Callbacks to invoke when this object is disposed.

Static Functions

Dispatches the given event on the ancestorsTree.

Parameters
target: !Object
The target to dispatch on.
e: (goog.events.Event|Object|string)
The event object.
opt_ancestorsTree: Array.<goog.events.Listenable>=
The ancestors + tree of the target, in reverse order from the closest ancestor + to the root event target. May be null if the target has no ancestor.
Returns
If anyone called preventDefault on the event object (or + if any of the listeners returns false) this will also return false.

Static Properties

An artificial cap on the number of ancestors you can have. This is mainly + for loop detection.

\ No newline at end of file diff --git a/docs/class_goog_events_Listener.html b/docs/class_goog_events_Listener.html new file mode 100644 index 0000000..7b718bc --- /dev/null +++ b/docs/class_goog_events_Listener.html @@ -0,0 +1,6 @@ +goog.events.Listener

Class goog.events.Listener

code »
All implemented interfaces:
goog.events.ListenableKey

Simple class that stores information about a listener

Constructor

goog.events.Listener ( listener, proxy, src, type, capture, opt_handler )
Parameters
listener: !Function
Callback function.
proxy: Function
Wrapper for the listener that patches the event.
src: (EventTarget|goog.events.Listenable)
Source object for + the event.
type: string
Event type.
capture: boolean
Whether in capture or bubble phase.
opt_handler: Object=
Object in whose context to execute the callback.
Show:

Instance Methods

Marks this listener as removed. This also remove references held by + this listener object (such as listener and event source).

Instance Properties

Whether to remove the listener after it has been called.

Whether the listener is being called in the capture or bubble phase

If monitoring the goog.events.Listener instances is enabled, stores the + creation stack trace of the Disposable instance.

Optional object whose context to execute the listener in

The key of the listener.

Callback function.

A wrapper over the original listener. This is used solely to + handle native browser events (it is used to simulate the capture + phase and to patch the event object).

Whether the listener has been removed.

Object or node that callback is listening to

The event type.

Compiler Constants

\ No newline at end of file diff --git a/docs/class_goog_events_ListenerMap.html b/docs/class_goog_events_ListenerMap.html new file mode 100644 index 0000000..2e5abb3 --- /dev/null +++ b/docs/class_goog_events_ListenerMap.html @@ -0,0 +1,23 @@ +goog.events.ListenerMap

Class goog.events.ListenerMap

code »

Creates a new listener map.

Constructor

goog.events.ListenerMap ( src )
Parameters
src: (EventTarget|goog.events.Listenable)
The src object.
Show:

Instance Methods

code »add ( type, listener, callOnce, opt_useCapture, opt_listenerScope )goog.events.ListenableKey

Adds an event listener. A listener can only be added once to an + object and if it is added again the key for the listener is + returned. + + Note that a one-off listener will not change an existing listener, + if any. On the other hand a normal listener will change existing + one-off listener to become a normal listener.

Parameters
type: (string|!goog.events.EventId)
The listener event type.
listener: !Function
This listener callback method.
callOnce: boolean
Whether the listener is a one-off + listener.
opt_useCapture: boolean=
The capture mode of the listener.
opt_listenerScope: Object=
Object in whose scope to call the + listener.
Returns
Unique key for the listener.
code »getListener ( type, listener, capture, opt_listenerScope )goog.events.ListenableKey

Gets the goog.events.ListenableKey for the event or null if no such + listener is in use.

Parameters
type: (string|!goog.events.EventId)
The type of the listener + to retrieve.
listener: !Function
The listener function to get.
capture: boolean
Whether the listener is a capturing listener.
opt_listenerScope: Object=
Object in whose scope to call the + listener.
Returns
the found listener or null if not found.
Returns
Total number of registered listeners.

Gets all listeners that match the given type and capture mode. The + returned array is a copy (but the listener objects are not).

Parameters
type: (string|!goog.events.EventId)
The type of the listeners + to retrieve.
capture: boolean
The capture mode of the listeners to retrieve.
Returns
An array of matching + listeners.
Returns
The count of event types in this map that actually + have registered listeners.
code »hasListener ( opt_type, opt_capture )boolean

Whether there is a matching listener. If either the type or capture + parameters are unspecified, the function will match on the + remaining criteria.

Parameters
opt_type: (string|!goog.events.EventId)=
The type of the listener.
opt_capture: boolean=
The capture mode of the listener.
Returns
Whether there is an active listener matching + the requested type and/or capture phase.
code »remove ( type, listener, opt_useCapture, opt_listenerScope )boolean

Removes a matching listener.

Parameters
type: (string|!goog.events.EventId)
The listener event type.
listener: !Function
This listener callback method.
opt_useCapture: boolean=
The capture mode of the listener.
opt_listenerScope: Object=
Object in whose scope to call the + listener.
Returns
Whether any listener was removed.
code »removeAll ( opt_type )number

Removes all listeners from this map. If opt_type is provided, only + listeners that match the given type are removed.

Parameters
opt_type: (string|!goog.events.EventId)=
Type of event to remove.
Returns
Number of listeners removed.
code »removeByKey ( listener )boolean

Removes the given listener object.

Parameters
listener: goog.events.ListenableKey
The listener to remove.
Returns
Whether the listener is removed.

Instance Properties

Maps of event type to an array of listeners.

The count of types in this map that have registered listeners.

Static Functions

code »goog.events.ListenerMap.findListenerIndex_ ( listenerArray, listener, opt_useCapture, opt_listenerScope )number

Finds the index of a matching goog.events.Listener in the given + listenerArray.

Parameters
listenerArray: !Array
Array of listener.
listener: !Function
The listener function.
opt_useCapture: boolean=
The capture flag for the listener.
opt_listenerScope: Object=
The listener scope.
Returns
The index of the matching listener within the + listenerArray.
\ No newline at end of file diff --git a/docs/class_goog_iter_GroupByIterator_.html b/docs/class_goog_iter_GroupByIterator_.html new file mode 100644 index 0000000..c2e0c02 --- /dev/null +++ b/docs/class_goog_iter_GroupByIterator_.html @@ -0,0 +1,12 @@ +goog.iter.GroupByIterator_

Class goog.iter.GroupByIterator_.<KEY, VALUE>

code »
goog.iter.Iterator.<Array>
+  └ goog.iter.GroupByIterator_

Implements the goog.iter.groupBy iterator.

Constructor

goog.iter.GroupByIterator_ ( iterable, opt_keyFunc )
Parameters
iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
The + iterable to group.
opt_keyFunc: function(VALUE): KEY=
Optional function for + determining the key value for each group in the iterable. Default + is the identity function.
Show:

Instance Methods

Defined in goog.iter.GroupByIterator_

code »groupItems_ ( targetKey )!Array.<VALUE>

Performs the grouping of objects using the given key.

Parameters
targetKey: KEY
The target key object for the group.
Returns
An array of grouped objects.
code »keyFunc ( )KEY

A function for determining the key value for each element in the iterable. + If no function is provided, the identity function is used and returns the + element unchanged.

code »next ( )Array

Defined in goog.iter.Iterator.<Array>

code »__iterator__ ( opt_keys )!goog.iter.Iterator.<VALUE>

Returns the Iterator object itself. This is used to implement + the iterator protocol in JavaScript 1.7

Parameters
opt_keys: boolean=
Whether to return the keys or values. Default is + to only return the values. This is being used by the for-in loop (true) + and the for-each-in loop (false). Even though the param gives a hint + about what the iterator will return there is no guarantee that it will + return the keys when true is passed.
Returns
The object itself.

Instance Properties

Defined in goog.iter.GroupByIterator_

The current key visited during iteration.

The current value being added to the group.

The iterable to group, coerced to an iterator.

The target key for determining the start of a group.

Static Properties

\ No newline at end of file diff --git a/docs/class_goog_iter_Iterator.html b/docs/class_goog_iter_Iterator.html index 2d55016..c1ed2cd 100644 --- a/docs/class_goog_iter_Iterator.html +++ b/docs/class_goog_iter_Iterator.html @@ -1,11 +1,11 @@ -goog.iter.Iterator

Class goog.iter.Iterator

code »

Class/interface for iterators. An iterator needs to implement a next +goog.iter.Iterator

Class goog.iter.Iterator.<VALUE>

code »

Class/interface for iterators. An iterator needs to implement a 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

goog.iter.Iterator ( )
Show:

Instance Methods

Returns the Iterator object itself. This is used to implement + iterator or in case you are only targeting JavaScript 1.7 for in loops.

Constructor

goog.iter.Iterator ( )
Show:

Instance Methods

code »__iterator__ ( opt_keys )!goog.iter.Iterator.<VALUE>

Returns the Iterator object itself. This is used to implement the iterator protocol in JavaScript 1.7

Parameters
opt_keys: boolean=
Whether to return the keys or values. Default is to only return the values. This is being used by the for-in loop (true) and the for-each-in loop (false). Even though the param gives a hint about what the iterator will return there is no guarantee that it will - return the keys when true is passed.
Returns
The object itself.
code »next ( )*

Returns the next value of the iteration. This will throw the object - goog.iter#StopIteration when the iteration passes the end.

Returns
Any object or value.
\ No newline at end of file + return the keys when true is passed.Returns
The object itself.
code »next ( )VALUE

Returns the next value of the iteration. This will throw the object + goog.iter#StopIteration when the iteration passes the end.

Returns
Any object or value.
\ No newline at end of file diff --git a/docs/class_goog_json_Serializer.html b/docs/class_goog_json_Serializer.html index 94211df..9b83d58 100644 --- a/docs/class_goog_json_Serializer.html +++ b/docs/class_goog_json_Serializer.html @@ -1,4 +1,4 @@ -goog.json.Serializer

Class goog.json.Serializer

code »

Class that is used to serialize JSON objects to a string.

Constructor

goog.json.Serializer ( opt_replacer )
Parameters
opt_replacer: ?goog.json.Replacer=
Replacer.
Show:

Instance Methods

code »serialize ( object )string

Serializes an object or a value to a JSON string.

Parameters
object: *
The object to serialize.
Returns
A JSON string representation of the input.
Throws
if there are loops in the object graph.

Serializes an array to a JSON string

Parameters
arr: Array
The array to serialize.
sb: Array
Array used as a string builder.

Serializes a number to a JSON string

Parameters
n: number
The number to serialize.
sb: Array
Array used as a string builder.

Serializes an object to a JSON string

Parameters
obj: Object
The object to serialize.
sb: Array
Array used as a string builder.

Serializes a string to a JSON string

Parameters
s: string
The string to serialize.
sb: Array
Array used as a string builder.
code »serialize_ ( object, sb )

Serializes a generic value to a JSON string

Parameters
object: *
The object to serialize.
sb: Array
Array used as a string builder.
Throws
if there are loops in the object graph.

Instance Properties

Static Properties

Character mappings used internally for goog.string.quote

Regular expression used to match characters that need to be replaced. +goog.json.Serializer

Class goog.json.Serializer

code »

Class that is used to serialize JSON objects to a string.

Constructor

goog.json.Serializer ( opt_replacer )
Parameters
opt_replacer: ?goog.json.Replacer=
Replacer.
Show:

Instance Methods

code »serialize ( object )string

Serializes an object or a value to a JSON string.

Parameters
object: *
The object to serialize.
Returns
A JSON string representation of the input.
Throws
if there are loops in the object graph.

Serializes an array to a JSON string

Parameters
arr: Array
The array to serialize.
sb: Array
Array used as a string builder.

Serializes a generic value to a JSON string

Parameters
object: *
The object to serialize.
sb: Array
Array used as a string builder.
Throws
if there are loops in the object graph.

Serializes a number to a JSON string

Parameters
n: number
The number to serialize.
sb: Array
Array used as a string builder.

Serializes an object to a JSON string

Parameters
obj: Object
The object to serialize.
sb: Array
Array used as a string builder.

Serializes a string to a JSON string

Parameters
s: string
The string to serialize.
sb: Array
Array used as a string builder.

Instance Properties

Static Properties

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.

\ No newline at end of file + adjusts the regular expression accordingly.
\ No newline at end of file diff --git a/docs/class_goog_labs_testing_AllOfMatcher.html b/docs/class_goog_labs_testing_AllOfMatcher.html index 72579bf..b407e60 100644 --- a/docs/class_goog_labs_testing_AllOfMatcher.html +++ b/docs/class_goog_labs_testing_AllOfMatcher.html @@ -1,2 +1,2 @@ -goog.labs.testing.AllOfMatcher

Class goog.labs.testing.AllOfMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The AllOf matcher.

Constructor

goog.labs.testing.AllOfMatcher ( matchers )
Parameters
matchers: !Array
Input matchers.
Show:

Instance Methods

code »describe ( actualValue )string

Describes why the matcher failed. The returned string is a concatenation of - all the failed matchers' error strings.

Parameters
actualValue
code »matches ( actualValue )boolean

Determines if all of the matchers match the input value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.AllOfMatcher

Class goog.labs.testing.AllOfMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The AllOf matcher.

Constructor

goog.labs.testing.AllOfMatcher ( matchers )
Parameters
matchers: !Array
Input matchers.
Show:

Instance Methods

code »describe ( actualValue )string

Describes why the matcher failed. The returned string is a concatenation of + all the failed matchers' error strings.

Parameters
actualValue
code »matches ( actualValue )boolean

Determines if all of the matchers match the input value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_AnyOfMatcher.html b/docs/class_goog_labs_testing_AnyOfMatcher.html index 0cd3102..dfa3331 100644 --- a/docs/class_goog_labs_testing_AnyOfMatcher.html +++ b/docs/class_goog_labs_testing_AnyOfMatcher.html @@ -1 +1 @@ -goog.labs.testing.AnyOfMatcher

Class goog.labs.testing.AnyOfMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The AnyOf matcher.

Constructor

goog.labs.testing.AnyOfMatcher ( matchers )
Parameters
matchers: !Array
Input matchers.
Show:

Instance Methods

code »describe ( actualValue )string

Describes why the matcher failed.

Parameters
actualValue
code »matches ( actualValue )boolean

Determines if any of the matchers matches the input value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.AnyOfMatcher

Class goog.labs.testing.AnyOfMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The AnyOf matcher.

Constructor

goog.labs.testing.AnyOfMatcher ( matchers )
Parameters
matchers: !Array
Input matchers.
Show:

Instance Methods

code »describe ( actualValue )string

Describes why the matcher failed.

Parameters
actualValue
code »matches ( actualValue )boolean

Determines if any of the matchers matches the input value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_CloseToMatcher.html b/docs/class_goog_labs_testing_CloseToMatcher.html index ac2e402..790d379 100644 --- a/docs/class_goog_labs_testing_CloseToMatcher.html +++ b/docs/class_goog_labs_testing_CloseToMatcher.html @@ -1 +1 @@ -goog.labs.testing.CloseToMatcher

Class goog.labs.testing.CloseToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The CloseTo matcher.

Constructor

goog.labs.testing.CloseToMatcher ( value, range )
Parameters
value: number
The value to compare.
range: number
The range to check within.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is within a certain range of the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.CloseToMatcher

Class goog.labs.testing.CloseToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The CloseTo matcher.

Constructor

goog.labs.testing.CloseToMatcher ( value, range )
Parameters
value: number
The value to compare.
range: number
The range to check within.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is within a certain range of the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_ContainsStringMatcher.html b/docs/class_goog_labs_testing_ContainsStringMatcher.html index 66d6afb..772335e 100644 --- a/docs/class_goog_labs_testing_ContainsStringMatcher.html +++ b/docs/class_goog_labs_testing_ContainsStringMatcher.html @@ -1 +1 @@ -goog.labs.testing.ContainsStringMatcher

Class goog.labs.testing.ContainsStringMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The ContainsString matcher.

Constructor

goog.labs.testing.ContainsStringMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string contains the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.ContainsStringMatcher

Class goog.labs.testing.ContainsStringMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The ContainsString matcher.

Constructor

goog.labs.testing.ContainsStringMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string contains the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_EndsWithMatcher.html b/docs/class_goog_labs_testing_EndsWithMatcher.html index 32b7c41..b6e434f 100644 --- a/docs/class_goog_labs_testing_EndsWithMatcher.html +++ b/docs/class_goog_labs_testing_EndsWithMatcher.html @@ -1 +1 @@ -goog.labs.testing.EndsWithMatcher

Class goog.labs.testing.EndsWithMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The EndsWith matcher.

Constructor

goog.labs.testing.EndsWithMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string ends with the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.EndsWithMatcher

Class goog.labs.testing.EndsWithMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The EndsWith matcher.

Constructor

goog.labs.testing.EndsWithMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string ends with the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_EqualToIgnoringWhitespaceMatcher.html b/docs/class_goog_labs_testing_EqualToIgnoringWhitespaceMatcher.html index 04e6f59..f82d598 100644 --- a/docs/class_goog_labs_testing_EqualToIgnoringWhitespaceMatcher.html +++ b/docs/class_goog_labs_testing_EqualToIgnoringWhitespaceMatcher.html @@ -1 +1 @@ -goog.labs.testing.EqualToIgnoringWhitespaceMatcher

Class goog.labs.testing.EqualToIgnoringWhitespaceMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The EqualToIgnoringWhitespace matcher.

Constructor

goog.labs.testing.EqualToIgnoringWhitespaceMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string contains the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.EqualToIgnoringWhitespaceMatcher

Class goog.labs.testing.EqualToIgnoringWhitespaceMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The EqualToIgnoringWhitespace matcher.

Constructor

goog.labs.testing.EqualToIgnoringWhitespaceMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string contains the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_EqualToMatcher.html b/docs/class_goog_labs_testing_EqualToMatcher.html index 4dd288f..81c5dcb 100644 --- a/docs/class_goog_labs_testing_EqualToMatcher.html +++ b/docs/class_goog_labs_testing_EqualToMatcher.html @@ -1 +1 @@ -goog.labs.testing.EqualToMatcher

Class goog.labs.testing.EqualToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The EqualTo matcher.

Constructor

goog.labs.testing.EqualToMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is equal to the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.EqualToMatcher

Class goog.labs.testing.EqualToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The EqualTo matcher.

Constructor

goog.labs.testing.EqualToMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is equal to the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_EqualsMatcher.html b/docs/class_goog_labs_testing_EqualsMatcher.html index 3147f62..ab302cc 100644 --- a/docs/class_goog_labs_testing_EqualsMatcher.html +++ b/docs/class_goog_labs_testing_EqualsMatcher.html @@ -1 +1 @@ -goog.labs.testing.EqualsMatcher

Class goog.labs.testing.EqualsMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The Equals matcher.

Constructor

goog.labs.testing.EqualsMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string is equal to the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.EqualsMatcher

Class goog.labs.testing.EqualsMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The Equals matcher.

Constructor

goog.labs.testing.EqualsMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string is equal to the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_GreaterThanEqualToMatcher.html b/docs/class_goog_labs_testing_GreaterThanEqualToMatcher.html index 8c1b81e..75cfdab 100644 --- a/docs/class_goog_labs_testing_GreaterThanEqualToMatcher.html +++ b/docs/class_goog_labs_testing_GreaterThanEqualToMatcher.html @@ -1 +1 @@ -goog.labs.testing.GreaterThanEqualToMatcher

Class goog.labs.testing.GreaterThanEqualToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The GreaterThanEqualTo matcher.

Constructor

goog.labs.testing.GreaterThanEqualToMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is greater than equal to the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.GreaterThanEqualToMatcher

Class goog.labs.testing.GreaterThanEqualToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The GreaterThanEqualTo matcher.

Constructor

goog.labs.testing.GreaterThanEqualToMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is greater than equal to the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_GreaterThanMatcher.html b/docs/class_goog_labs_testing_GreaterThanMatcher.html index d54c332..841df16 100644 --- a/docs/class_goog_labs_testing_GreaterThanMatcher.html +++ b/docs/class_goog_labs_testing_GreaterThanMatcher.html @@ -1 +1 @@ -goog.labs.testing.GreaterThanMatcher

Class goog.labs.testing.GreaterThanMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The GreaterThan matcher.

Constructor

goog.labs.testing.GreaterThanMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is greater than the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.GreaterThanMatcher

Class goog.labs.testing.GreaterThanMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The GreaterThan matcher.

Constructor

goog.labs.testing.GreaterThanMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is greater than the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_HasPropertyMatcher.html b/docs/class_goog_labs_testing_HasPropertyMatcher.html index c92f19f..457b6c5 100644 --- a/docs/class_goog_labs_testing_HasPropertyMatcher.html +++ b/docs/class_goog_labs_testing_HasPropertyMatcher.html @@ -1 +1 @@ -goog.labs.testing.HasPropertyMatcher

Class goog.labs.testing.HasPropertyMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The HasProperty matcher.

Constructor

goog.labs.testing.HasPropertyMatcher ( property )
Parameters
property: string
Name of the property to test.
Show:

Instance Methods

code »describe ( actualObject )string
Parameters
actualObject
code »matches ( actualObject )boolean

Determines if an object has a property.

Parameters
actualObject

Instance Properties

\ No newline at end of file +goog.labs.testing.HasPropertyMatcher

Class goog.labs.testing.HasPropertyMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The HasProperty matcher.

Constructor

goog.labs.testing.HasPropertyMatcher ( property )
Parameters
property: string
Name of the property to test.
Show:

Instance Methods

code »describe ( actualObject )string
Parameters
actualObject
code »matches ( actualObject )boolean

Determines if an object has a property.

Parameters
actualObject

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_InstanceOfMatcher.html b/docs/class_goog_labs_testing_InstanceOfMatcher.html index f95c046..255bd2e 100644 --- a/docs/class_goog_labs_testing_InstanceOfMatcher.html +++ b/docs/class_goog_labs_testing_InstanceOfMatcher.html @@ -1 +1 @@ -goog.labs.testing.InstanceOfMatcher

Class goog.labs.testing.InstanceOfMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The InstanceOf matcher.

Constructor

goog.labs.testing.InstanceOfMatcher ( object )
Parameters
object: !Object
The expected class object.
Show:

Instance Methods

code »describe ( actualObject )string
Parameters
actualObject
code »matches ( actualObject )boolean

Determines if an object is an instance of another object.

Parameters
actualObject

Instance Properties

\ No newline at end of file +goog.labs.testing.InstanceOfMatcher

Class goog.labs.testing.InstanceOfMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The InstanceOf matcher.

Constructor

goog.labs.testing.InstanceOfMatcher ( object )
Parameters
object: !Object
The expected class object.
Show:

Instance Methods

code »describe ( actualObject )string
Parameters
actualObject
code »matches ( actualObject )boolean

Determines if an object is an instance of another object.

Parameters
actualObject

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_IsNotMatcher.html b/docs/class_goog_labs_testing_IsNotMatcher.html index 90d8178..a36c7ce 100644 --- a/docs/class_goog_labs_testing_IsNotMatcher.html +++ b/docs/class_goog_labs_testing_IsNotMatcher.html @@ -1 +1 @@ -goog.labs.testing.IsNotMatcher

Class goog.labs.testing.IsNotMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsNot matcher.

Constructor

goog.labs.testing.IsNotMatcher ( matcher )
Parameters
matcher: !goog.labs.testing.Matcher
The matcher to negate.
Show:

Instance Methods

code »describe ( actualValue )string

Describes why the matcher failed.

Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value doesn't satisfy a matcher.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.IsNotMatcher

Class goog.labs.testing.IsNotMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsNot matcher.

Constructor

goog.labs.testing.IsNotMatcher ( matcher )
Parameters
matcher: !goog.labs.testing.Matcher
The matcher to negate.
Show:

Instance Methods

code »describe ( actualValue )string

Describes why the matcher failed.

Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value doesn't satisfy a matcher.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_IsNullMatcher.html b/docs/class_goog_labs_testing_IsNullMatcher.html index 1517849..2873c4f 100644 --- a/docs/class_goog_labs_testing_IsNullMatcher.html +++ b/docs/class_goog_labs_testing_IsNullMatcher.html @@ -1 +1 @@ -goog.labs.testing.IsNullMatcher

Class goog.labs.testing.IsNullMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsNull matcher.

Constructor

goog.labs.testing.IsNullMatcher ( )
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is null.

Parameters
actualValue
\ No newline at end of file +goog.labs.testing.IsNullMatcher

Class goog.labs.testing.IsNullMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsNull matcher.

Constructor

goog.labs.testing.IsNullMatcher ( )
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is null.

Parameters
actualValue
\ No newline at end of file diff --git a/docs/class_goog_labs_testing_IsNullOrUndefinedMatcher.html b/docs/class_goog_labs_testing_IsNullOrUndefinedMatcher.html index 513e9ce..40eff35 100644 --- a/docs/class_goog_labs_testing_IsNullOrUndefinedMatcher.html +++ b/docs/class_goog_labs_testing_IsNullOrUndefinedMatcher.html @@ -1 +1 @@ -goog.labs.testing.IsNullOrUndefinedMatcher

Class goog.labs.testing.IsNullOrUndefinedMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsNullOrUndefined matcher.

Constructor

goog.labs.testing.IsNullOrUndefinedMatcher ( )
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is null or undefined.

Parameters
actualValue
\ No newline at end of file +goog.labs.testing.IsNullOrUndefinedMatcher

Class goog.labs.testing.IsNullOrUndefinedMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsNullOrUndefined matcher.

Constructor

goog.labs.testing.IsNullOrUndefinedMatcher ( )
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is null or undefined.

Parameters
actualValue
\ No newline at end of file diff --git a/docs/class_goog_labs_testing_IsUndefinedMatcher.html b/docs/class_goog_labs_testing_IsUndefinedMatcher.html index 7297062..e541471 100644 --- a/docs/class_goog_labs_testing_IsUndefinedMatcher.html +++ b/docs/class_goog_labs_testing_IsUndefinedMatcher.html @@ -1 +1 @@ -goog.labs.testing.IsUndefinedMatcher

Class goog.labs.testing.IsUndefinedMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsUndefined matcher.

Constructor

goog.labs.testing.IsUndefinedMatcher ( )
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is undefined.

Parameters
actualValue
\ No newline at end of file +goog.labs.testing.IsUndefinedMatcher

Class goog.labs.testing.IsUndefinedMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The IsUndefined matcher.

Constructor

goog.labs.testing.IsUndefinedMatcher ( )
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input value is undefined.

Parameters
actualValue
\ No newline at end of file diff --git a/docs/class_goog_labs_testing_LessThanEqualToMatcher.html b/docs/class_goog_labs_testing_LessThanEqualToMatcher.html index 33ae0fc..ffd26c3 100644 --- a/docs/class_goog_labs_testing_LessThanEqualToMatcher.html +++ b/docs/class_goog_labs_testing_LessThanEqualToMatcher.html @@ -1 +1 @@ -goog.labs.testing.LessThanEqualToMatcher

Class goog.labs.testing.LessThanEqualToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The LessThanEqualTo matcher.

Constructor

goog.labs.testing.LessThanEqualToMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is less than or equal to the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.LessThanEqualToMatcher

Class goog.labs.testing.LessThanEqualToMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The LessThanEqualTo matcher.

Constructor

goog.labs.testing.LessThanEqualToMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is less than or equal to the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_LessThanMatcher.html b/docs/class_goog_labs_testing_LessThanMatcher.html index 66195f9..3dc64a1 100644 --- a/docs/class_goog_labs_testing_LessThanMatcher.html +++ b/docs/class_goog_labs_testing_LessThanMatcher.html @@ -1 +1 @@ -goog.labs.testing.LessThanMatcher

Class goog.labs.testing.LessThanMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The lessThan matcher.

Constructor

goog.labs.testing.LessThanMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is less than the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.LessThanMatcher

Class goog.labs.testing.LessThanMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The lessThan matcher.

Constructor

goog.labs.testing.LessThanMatcher ( value )
Parameters
value: number
The value to compare.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if the input value is less than the expected value.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_MatcherError.html b/docs/class_goog_labs_testing_MatcherError.html index deb71c5..8edf8d8 100644 --- a/docs/class_goog_labs_testing_MatcherError.html +++ b/docs/class_goog_labs_testing_MatcherError.html @@ -1,3 +1,3 @@ -goog.labs.testing.MatcherError

Class goog.labs.testing.MatcherError

code »
Error
+goog.labs.testing.MatcherError

Class goog.labs.testing.MatcherError

code »
Errorgoog.debug.Error
-      └ goog.labs.testing.MatcherError

Error thrown when a Matcher fails to match the input value.

Constructor

goog.labs.testing.MatcherError ( opt_message )
Parameters
opt_message: string=
The error message.
Show:

Static Properties

\ No newline at end of file + └ goog.labs.testing.MatcherError

Error thrown when a Matcher fails to match the input value.

Constructor

goog.labs.testing.MatcherError ( opt_message )
Parameters
opt_message: string=
The error message.
Show:

Static Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_ObjectEqualsMatcher.html b/docs/class_goog_labs_testing_ObjectEqualsMatcher.html index 8d0dc16..12d8f28 100644 --- a/docs/class_goog_labs_testing_ObjectEqualsMatcher.html +++ b/docs/class_goog_labs_testing_ObjectEqualsMatcher.html @@ -1 +1 @@ -goog.labs.testing.ObjectEqualsMatcher

Class goog.labs.testing.ObjectEqualsMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The Equals matcher.

Constructor

goog.labs.testing.ObjectEqualsMatcher ( expectedObject )
Parameters
expectedObject: !Object
The expected object.
Show:

Instance Methods

code »describe ( actualObject )string
Parameters
actualObject
code »matches ( actualObject )boolean

Determines if two objects are the same.

Parameters
actualObject

Instance Properties

\ No newline at end of file +goog.labs.testing.ObjectEqualsMatcher

Class goog.labs.testing.ObjectEqualsMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The Equals matcher.

Constructor

goog.labs.testing.ObjectEqualsMatcher ( expectedObject )
Parameters
expectedObject: !Object
The expected object.
Show:

Instance Methods

code »describe ( actualObject )string
Parameters
actualObject
code »matches ( actualObject )boolean

Determines if two objects are the same.

Parameters
actualObject

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_RegexMatcher.html b/docs/class_goog_labs_testing_RegexMatcher.html index 57c53c8..db2df2e 100644 --- a/docs/class_goog_labs_testing_RegexMatcher.html +++ b/docs/class_goog_labs_testing_RegexMatcher.html @@ -1 +1 @@ -goog.labs.testing.RegexMatcher

Class goog.labs.testing.RegexMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The MatchesRegex matcher.

Constructor

goog.labs.testing.RegexMatcher ( regex )
Parameters
regex: !RegExp
The expected regex.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string is equal to the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.RegexMatcher

Class goog.labs.testing.RegexMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The MatchesRegex matcher.

Constructor

goog.labs.testing.RegexMatcher ( regex )
Parameters
regex: !RegExp
The expected regex.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string is equal to the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_StartsWithMatcher.html b/docs/class_goog_labs_testing_StartsWithMatcher.html index e73ad5a..df9f738 100644 --- a/docs/class_goog_labs_testing_StartsWithMatcher.html +++ b/docs/class_goog_labs_testing_StartsWithMatcher.html @@ -1 +1 @@ -goog.labs.testing.StartsWithMatcher

Class goog.labs.testing.StartsWithMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The StartsWith matcher.

Constructor

goog.labs.testing.StartsWithMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string starts with the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.StartsWithMatcher

Class goog.labs.testing.StartsWithMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The StartsWith matcher.

Constructor

goog.labs.testing.StartsWithMatcher ( value )
Parameters
value: string
The expected string.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string starts with the expected string.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_labs_testing_StringContainsInOrderMatcher.html b/docs/class_goog_labs_testing_StringContainsInOrderMatcher.html index 183a59b..02b0b87 100644 --- a/docs/class_goog_labs_testing_StringContainsInOrderMatcher.html +++ b/docs/class_goog_labs_testing_StringContainsInOrderMatcher.html @@ -1 +1 @@ -goog.labs.testing.StringContainsInOrderMatcher

Class goog.labs.testing.StringContainsInOrderMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The StringContainsInOrdermatcher.

Constructor

goog.labs.testing.StringContainsInOrderMatcher ( values )
Parameters
values: Array.<string>
The expected string values.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string contains, in order, the expected array of strings.

Parameters
actualValue

Instance Properties

\ No newline at end of file +goog.labs.testing.StringContainsInOrderMatcher

Class goog.labs.testing.StringContainsInOrderMatcher

code »
All implemented interfaces:
goog.labs.testing.Matcher

The StringContainsInOrdermatcher.

Constructor

goog.labs.testing.StringContainsInOrderMatcher ( values )
Parameters
values: Array.<string>
The expected string values.
Show:

Instance Methods

code »describe ( actualValue )string
Parameters
actualValue
code »matches ( actualValue )boolean

Determines if input string contains, in order, the expected array of strings.

Parameters
actualValue

Instance Properties

\ No newline at end of file diff --git a/docs/class_goog_math_Box.html b/docs/class_goog_math_Box.html new file mode 100644 index 0000000..84531c7 --- /dev/null +++ b/docs/class_goog_math_Box.html @@ -0,0 +1,23 @@ +goog.math.Box

Class goog.math.Box

code »

Class for representing a box. A box is specified as a top, right, bottom, + and left. A box is useful for representing margins and padding. + + This class assumes 'screen coordinates': larger Y coordinates are further + from the top of the screen.

Constructor

goog.math.Box ( top, right, bottom, left )
Parameters
top: number
Top.
right: number
Right.
bottom: number
Bottom.
left: number
Left.
Show:

Instance Methods

Rounds the fields to the next larger integer values.

Returns
This box with ceil'd fields.

Creates a copy of the box with the same dimensions.

Returns
A clone of this Box.
code »contains ( other )boolean

Returns whether the box contains a coordinate or another box.

Parameters
other: (goog.math.Coordinate|goog.math.Box)
A Coordinate or a Box.
Returns
Whether the box contains the coordinate or other box.
code »expand ( top, opt_right, opt_bottom, opt_left )!goog.math.Box

Expands box with the given margins.

Parameters
top: (number|goog.math.Box)
Top margin or box with all margins.
opt_right: number=
Right margin.
opt_bottom: number=
Bottom margin.
opt_left: number=
Left margin.
Returns
A reference to this Box.

Expand this box to include another box. + NOTE(user): This is used in code that needs to be very fast, please don't + add functionality to this function at the expense of speed (variable + arguments, accepting multiple argument types, etc).

Parameters
box: goog.math.Box
The box to include in this one.

Rounds the fields to the next smaller integer values.

Returns
This box with floored fields.
Returns
height The height of this Box.
Returns
width The width of this Box.

Rounds the fields to nearest integer values.

Returns
This box with rounded fields.
code »scale ( sx, opt_sy )!goog.math.Box

Scales this coordinate by the given scale factors. The x and y dimension + values are scaled by sx and opt_sy respectively. + If opt_sy is not given, then sx is used for both x and y.

Parameters
sx: number
The scale factor to use for the x dimension.
opt_sy: number=
The scale factor to use for the y dimension.
Returns
This box after scaling.

Returns a nice string representing the box.

Returns
In the form (50t, 73r, 24b, 13l).
code »translate ( tx, opt_ty )!goog.math.Box

Translates this box by the given offsets. If a goog.math.Coordinate + is given, then the left and right values are translated by the coordinate's + x value and the top and bottom values are translated by the coordinate's y + value. Otherwise, tx and opt_ty are used to translate the x + and y dimension values.

Parameters
tx: (number|goog.math.Coordinate)
The value to translate the x + dimension values by or the the coordinate to translate this box by.
opt_ty: number=
The value to translate y dimension values by.
Returns
This box after translating.

Instance Properties

Static Functions

Creates a Box by bounding a collection of goog.math.Coordinate objects

Parameters
var_args: ...goog.math.Coordinate
Coordinates to be included inside + the box.
Returns
A Box containing all the specified Coordinates.

Returns whether a box contains a coordinate or another box.

Parameters
box: goog.math.Box
A Box.
other: (goog.math.Coordinate|goog.math.Box)
A Coordinate or a Box.
Returns
Whether the box contains the coordinate or other box.

Returns the distance between a coordinate and the nearest corner/side of a + box. Returns zero if the coordinate is inside the box.

Parameters
box: goog.math.Box
A Box.
coord: goog.math.Coordinate
A Coordinate.
Returns
The distance between coord and the nearest + corner/side of box, or zero if coord is inside + box.

Compares boxes for equality.

Parameters
a: goog.math.Box
A Box.
b: goog.math.Box
A Box.
Returns
True iff the boxes are equal, or if both are null.

Returns whether two boxes intersect.

Parameters
a: goog.math.Box
A Box.
b: goog.math.Box
A second Box.
Returns
Whether the boxes intersect.

Returns whether two boxes would intersect with additional padding.

Parameters
a: goog.math.Box
A Box.
b: goog.math.Box
A second Box.
padding: number
The additional padding.
Returns
Whether the boxes intersect.

Returns the relative x position of a coordinate compared to a box. Returns + zero if the coordinate is inside the box.

Parameters
box: goog.math.Box
A Box.
coord: goog.math.Coordinate
A Coordinate.
Returns
The x position of coord relative to the nearest + side of box, or zero if coord is inside box.

Returns the relative y position of a coordinate compared to a box. Returns + zero if the coordinate is inside the box.

Parameters
box: goog.math.Box
A Box.
coord: goog.math.Coordinate
A Coordinate.
Returns
The y position of coord relative to the nearest + side of box, or zero if coord is inside box.
\ No newline at end of file diff --git a/docs/class_goog_math_Coordinate.html b/docs/class_goog_math_Coordinate.html new file mode 100644 index 0000000..c1e4989 --- /dev/null +++ b/docs/class_goog_math_Coordinate.html @@ -0,0 +1,22 @@ +goog.math.Coordinate

Class goog.math.Coordinate

code »

Class for representing coordinates and positions.

Constructor

goog.math.Coordinate ( opt_x, opt_y )
Parameters
opt_x: number=
Left, defaults to 0.
opt_y: number=
Top, defaults to 0.
Show:

Instance Methods

Rounds the x and y fields to the next larger integer values.

Returns
This coordinate with ceil'd fields.

Returns a new copy of the coordinate.

Returns
A clone of this coordinate.

Rounds the x and y fields to the next smaller integer values.

Returns
This coordinate with floored fields.
code »rotateDegrees ( degrees, opt_center )

Rotates this coordinate clockwise about the origin (or, optionally, the given + center) by the given angle, in degrees.

Parameters
degrees: number
The angle by which to rotate this coordinate + clockwise about the given center, in degrees.
opt_center: !goog.math.Coordinate=
The center of rotation. Defaults + to (0, 0) if not given.
code »rotateRadians ( radians, opt_center )

Rotates this coordinate clockwise about the origin (or, optionally, the given + center) by the given angle, in radians.

Parameters
radians: number
The angle by which to rotate this coordinate + clockwise about the given center, in radians.
opt_center: !goog.math.Coordinate=
The center of rotation. Defaults + to (0, 0) if not given.

Rounds the x and y fields to the nearest integer values.

Returns
This coordinate with rounded fields.

Scales this coordinate by the given scale factors. The x and y values are + scaled by sx and opt_sy respectively. If opt_sy + is not given, then sx is used for both x and y.

Parameters
sx: number
The scale factor to use for the x dimension.
opt_sy: number=
The scale factor to use for the y dimension.
Returns
This coordinate after scaling.

Returns a nice string representing the coordinate.

Returns
In the form (50, 73).

Translates this box by the given offsets. If a goog.math.Coordinate + is given, then the x and y values are translated by the coordinate's x and y. + Otherwise, x and y are translated by tx and opt_ty + respectively.

Parameters
tx: (number|goog.math.Coordinate)
The value to translate x by or the + the coordinate to translate this coordinate by.
opt_ty: number=
The value to translate y by.
Returns
This coordinate after translating.

Instance Properties

X-value

Y-value

Static Functions

Returns the angle from the origin to a coordinate.

Parameters
a: !goog.math.Coordinate
A Coordinate.
Returns
The angle, in degrees, clockwise from the positive X + axis to a.

Returns the difference between two coordinates as a new + goog.math.Coordinate.

Parameters
a: !goog.math.Coordinate
A Coordinate.
b: !goog.math.Coordinate
A Coordinate.
Returns
A Coordinate representing the difference + between a and b.

Returns the distance between two coordinates.

Parameters
a: !goog.math.Coordinate
A Coordinate.
b: !goog.math.Coordinate
A Coordinate.
Returns
The distance between a and b.

Compares coordinates for equality.

Parameters
a: goog.math.Coordinate
A Coordinate.
b: goog.math.Coordinate
A Coordinate.
Returns
True iff the coordinates are equal, or if both are null.

Returns the magnitude of a coordinate.

Parameters
a: !goog.math.Coordinate
A Coordinate.
Returns
The distance between the origin and a.

Returns the squared distance between two coordinates. Squared distances can + be used for comparisons when the actual value is not required. + + Performance note: eliminating the square root is an optimization often used + in lower-level languages, but the speed difference is not nearly as + pronounced in JavaScript (only a few percent.)

Parameters
a: !goog.math.Coordinate
A Coordinate.
b: !goog.math.Coordinate
A Coordinate.
Returns
The squared distance between a and b.

Returns the sum of two coordinates as a new goog.math.Coordinate.

Parameters
a: !goog.math.Coordinate
A Coordinate.
b: !goog.math.Coordinate
A Coordinate.
Returns
A Coordinate representing the sum of the two + coordinates.
\ No newline at end of file diff --git a/docs/class_goog_math_Rect.html b/docs/class_goog_math_Rect.html new file mode 100644 index 0000000..3b077eb --- /dev/null +++ b/docs/class_goog_math_Rect.html @@ -0,0 +1,34 @@ +goog.math.Rect

Class goog.math.Rect

code »

Class for representing rectangular regions.

Constructor

goog.math.Rect ( x, y, w, h )
Parameters
x: number
Left.
y: number
Top.
w: number
Width.
h: number
Height.
Show:

Instance Methods

Expand this rectangle to also include the area of the given rectangle.

Parameters
rect: goog.math.Rect
The other rectangle.

Rounds the fields to the next larger integer values.

Returns
This rectangle with ceil'd fields.
Returns
A new copy of this Rectangle.
code »contains ( another )boolean

Tests whether this rectangle entirely contains another rectangle or + coordinate.

Parameters
another: (goog.math.Rect|goog.math.Coordinate)
The rectangle or + coordinate to test for containment.
Returns
Whether this rectangle contains given rectangle or + coordinate.
code »difference ( rect )!Array

Computes the difference regions between this rectangle and rect. The + return value is an array of 0 to 4 rectangles defining the remaining regions + of this rectangle after the other has been subtracted.

Parameters
rect: goog.math.Rect
A Rectangle.
Returns
An array with 0 to 4 rectangles which + together define the difference area of rectangle a minus rectangle b.
code »distance ( point )number
Parameters
point: !goog.math.Coordinate
A coordinate.
Returns
The distance between the point and the closest point + inside the rectangle. Returns 0 if the point is inside the rectangle.

Rounds the fields to the next smaller integer values.

Returns
This rectangle with floored fields.
Returns
A new coordinate for the bottom-right corner + of the rectangle.
Returns
A new coordinate for the center of the + rectangle.
Returns
The size of this rectangle.
Returns
A new coordinate for the top-left corner of + the rectangle.

Computes the intersection of this rectangle and the rectangle parameter. If + there is no intersection, returns false and leaves this rectangle as is.

Parameters
rect: goog.math.Rect
A Rectangle.
Returns
True iff this rectangle intersects with the parameter.

Returns whether a rectangle intersects this rectangle.

Parameters
rect: goog.math.Rect
A rectangle.
Returns
Whether rect intersects this rectangle.

Rounds the fields to nearest integer values.

Returns
This rectangle with rounded fields.
code »scale ( sx, opt_sy )!goog.math.Rect

Scales this rectangle by the given scale factors. The left and width values + are scaled by sx and the top and height values are scaled by + opt_sy. If opt_sy is not given, then all fields are scaled + by sx.

Parameters
sx: number
The scale factor to use for the x dimension.
opt_sy: number=
The scale factor to use for the y dimension.
Returns
This rectangle after scaling.
Parameters
point: !goog.math.Coordinate
A coordinate.
Returns
The squared distance between the point and the closest + point inside the rectangle. Returns 0 if the point is inside the + rectangle.

Returns a new Box object with the same position and dimensions as this + rectangle.

Returns
A new Box representation of this Rectangle.

Returns a nice string representing size and dimensions of rectangle.

Returns
In the form (50, 73 - 75w x 25h).
code »translate ( tx, opt_ty )!goog.math.Rect

Translates this rectangle by the given offsets. If a + goog.math.Coordinate is given, then the left and top values are + translated by the coordinate's x and y values. Otherwise, top and left are + translated by tx and opt_ty respectively.

Parameters
tx: (number|goog.math.Coordinate)
The value to translate left by or the + the coordinate to translate this rect by.
opt_ty: number=
The value to translate top by.
Returns
This rectangle after translating.

Instance Properties

Static Functions

Returns a new rectangle which completely contains both input rectangles.

Parameters
a: goog.math.Rect
A rectangle.
b: goog.math.Rect
A rectangle.
Returns
A new bounding rect, or null if either rect is + null.

Creates a new Rect object with the same position and dimensions as a given + Box. Note that this is only the inverse of toBox if left/top are defined.

Parameters
box: goog.math.Box
A box.
Returns
A new Rect initialized with the box's position + and size.

Computes the difference regions between two rectangles. The return value is + an array of 0 to 4 rectangles defining the remaining regions of the first + rectangle after the second has been subtracted.

Parameters
a: goog.math.Rect
A Rectangle.
b: goog.math.Rect
A Rectangle.
Returns
An array with 0 to 4 rectangles which + together define the difference area of rectangle a minus rectangle b.

Compares rectangles for equality.

Parameters
a: goog.math.Rect
A Rectangle.
b: goog.math.Rect
A Rectangle.
Returns
True iff the rectangles have the same left, top, width, + and height, or if both are null.

Returns the intersection of two rectangles. Two rectangles intersect if they + touch at all, for example, two zero width and height rectangles would + intersect if they had the same top and left.

Parameters
a: goog.math.Rect
A Rectangle.
b: goog.math.Rect
A Rectangle.
Returns
A new intersection rect (even if width and height + are 0), or null if there is no intersection.

Returns whether two rectangles intersect. Two rectangles intersect if they + touch at all, for example, two zero width and height rectangles would + intersect if they had the same top and left.

Parameters
a: goog.math.Rect
A Rectangle.
b: goog.math.Rect
A Rectangle.
Returns
Whether a and b intersect.
\ No newline at end of file diff --git a/docs/class_goog_math_Size.html b/docs/class_goog_math_Size.html new file mode 100644 index 0000000..efb7ff6 --- /dev/null +++ b/docs/class_goog_math_Size.html @@ -0,0 +1,10 @@ +goog.math.Size

Class goog.math.Size

code »

Class for representing sizes consisting of a width and height. Undefined + width and height support is deprecated and results in compiler warning.

Constructor

goog.math.Size ( width, height )
Parameters
width: number
Width.
height: number
Height.
Show:

Instance Methods

Returns
The area of the size (width * height).
Returns
The ratio of the size's width to its height.

Clamps the width and height parameters upward to integer values.

Returns
This size with ceil'd components.
Returns
A new copy of the Size.
code »fitsInside ( target )boolean
Parameters
target: !goog.math.Size
The target size.
Returns
True if this Size is the same size or smaller than the + target size in both dimensions.

Clamps the width and height parameters downward to integer values.

Returns
This size with floored components.
Returns
The longer of the two dimensions in the size.
Returns
The shorter of the two dimensions in the size.
Returns
True if the size has zero area, false if both dimensions + are non-zero numbers.
Returns
The perimeter of the size (width + height) * 2.

Rounds the width and height parameters to integer values.

Returns
This size with rounded components.
code »scale ( sx, opt_sy )!goog.math.Size

Scales this size by the given scale factors. The width and height are scaled + by sx and opt_sy respectively. If opt_sy is not + given, then sx is used for both the width and height.

Parameters
sx: number
The scale factor to use for the width.
opt_sy: number=
The scale factor to use for the height.
Returns
This Size object after scaling.

Uniformly scales the size to fit inside the dimensions of a given size. The + original aspect ratio will be preserved. + + This function assumes that both Sizes contain strictly positive dimensions.

Parameters
target: !goog.math.Size
The target size.
Returns
This Size object, after optional scaling.

Returns a nice string representing size.

Returns
In the form (50 x 73).

Instance Properties

Static Functions

Compares sizes for equality.

Parameters
a: goog.math.Size
A Size.
b: goog.math.Size
A Size.
Returns
True iff the sizes have equal widths and equal + heights, or if both are null.
\ No newline at end of file diff --git a/docs/class_goog_net_DefaultXmlHttpFactory.html b/docs/class_goog_net_DefaultXmlHttpFactory.html index dbe1b20..054fd93 100644 --- a/docs/class_goog_net_DefaultXmlHttpFactory.html +++ b/docs/class_goog_net_DefaultXmlHttpFactory.html @@ -1,4 +1,4 @@ -goog.net.DefaultXmlHttpFactory

Class goog.net.DefaultXmlHttpFactory

code »
goog.net.XmlHttpFactory
+goog.net.DefaultXmlHttpFactory

Class goog.net.DefaultXmlHttpFactory

code »
goog.net.XmlHttpFactory
   └ goog.net.DefaultXmlHttpFactory

Default factory to use when creating xhr objects. You probably shouldn't be - instantiating this directly, but rather using it via goog.net.XmlHttp.

Constructor

goog.net.DefaultXmlHttpFactory ( )
Show:

Instance Methods

Defined in goog.net.DefaultXmlHttpFactory

code »createInstance ( )(GearsHttpRequest|XMLHttpRequest)

Initialize the private state used by other functions.

Returns
The ActiveX PROG ID string to use to create xhr's in IE.
code »internalGetOptions ( )(Object|null)

Defined in goog.net.XmlHttpFactory

Returns
Options describing how xhr objects obtained from this - factory should be used.

Instance Properties

Defined in goog.net.DefaultXmlHttpFactory

The ActiveX PROG ID string to use to create xhr's in IE. Lazily initialized.

Defined in goog.net.XmlHttpFactory

Cache of options - we only actually call internalGetOptions once.

Static Properties

\ No newline at end of file + instantiating this directly, but rather using it via goog.net.XmlHttp.

Constructor

goog.net.DefaultXmlHttpFactory ( )
Show:

Instance Methods

Defined in goog.net.DefaultXmlHttpFactory

code »createInstance ( )(XMLHttpRequest|goog.net.XhrLike)

Initialize the private state used by other functions.

Returns
The ActiveX PROG ID string to use to create xhr's in IE.
code »internalGetOptions ( )(Object|null)

Defined in goog.net.XmlHttpFactory

Returns
Options describing how xhr objects obtained from this + factory should be used.

Instance Properties

Defined in goog.net.DefaultXmlHttpFactory

The ActiveX PROG ID string to use to create xhr's in IE. Lazily initialized.

Defined in goog.net.XmlHttpFactory

Cache of options - we only actually call internalGetOptions once.

Static Properties

\ No newline at end of file diff --git a/docs/class_goog_net_WrapperXmlHttpFactory.html b/docs/class_goog_net_WrapperXmlHttpFactory.html index caf95bc..927086d 100644 --- a/docs/class_goog_net_WrapperXmlHttpFactory.html +++ b/docs/class_goog_net_WrapperXmlHttpFactory.html @@ -1,8 +1,7 @@ -goog.net.WrapperXmlHttpFactory

Class goog.net.WrapperXmlHttpFactory

code »
goog.net.XmlHttpFactory
+goog.net.WrapperXmlHttpFactory

Class goog.net.WrapperXmlHttpFactory

code »
goog.net.XmlHttpFactory
   └ goog.net.WrapperXmlHttpFactory

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.

Constructor

goog.net.WrapperXmlHttpFactory ( xhrFactory, optionsFactory )
Parameters
xhrFactory: function(): !(XMLHttpRequest|GearsHttpRequest)
A - function which returns a new XHR object.
optionsFactory: function(): !Object
A function which returns the - options associated with xhr objects from this factory.
Show:

Instance Methods

Defined in goog.net.WrapperXmlHttpFactory

code »createInstance ( )(GearsHttpRequest|XMLHttpRequest)
code »getOptions ( )(Object|null)
code »optionsFactory_ ( )Object

Options factory method.

code »xhrFactory_ ( )(GearsHttpRequest|XMLHttpRequest)

XHR factory method.

Defined in goog.net.XmlHttpFactory

Override this method in subclasses to preserve the caching offered by + with an unchanged signature.

Constructor

goog.net.WrapperXmlHttpFactory ( xhrFactory, optionsFactory )
Parameters
xhrFactory: function(): !goog.net.XhrLike.OrNative
A function which returns a new XHR object.
optionsFactory: function(): !Object
A function which returns the + options associated with xhr objects from this factory.
Show:

Instance Methods

Defined in goog.net.WrapperXmlHttpFactory

code »createInstance ( )(XMLHttpRequest|goog.net.XhrLike)
code »getOptions ( )(Object|null)
code »optionsFactory_ ( )Object

Options factory method.

code »xhrFactory_ ( )(XMLHttpRequest|goog.net.XhrLike)

XHR factory method.

Defined in goog.net.XmlHttpFactory

Override this method in subclasses to preserve the caching offered by getOptions().

Returns
Options describing how xhr objects obtained from this - factory should be used.

Instance Properties

Defined in goog.net.XmlHttpFactory

Cache of options - we only actually call internalGetOptions once.

Static Properties

\ No newline at end of file + factory should be used.

Instance Properties

Defined in goog.net.XmlHttpFactory

Cache of options - we only actually call internalGetOptions once.

Static Properties

\ No newline at end of file diff --git a/docs/class_goog_net_XmlHttpFactory.html b/docs/class_goog_net_XmlHttpFactory.html index 7850a12..849e5ee 100644 --- a/docs/class_goog_net_XmlHttpFactory.html +++ b/docs/class_goog_net_XmlHttpFactory.html @@ -1,4 +1,4 @@ -goog.net.XmlHttpFactory

Class goog.net.XmlHttpFactory

code »

Abstract base class for an XmlHttpRequest factory.

Constructor

goog.net.XmlHttpFactory ( )
Show:

Instance Methods

code »createInstance ( )!(XMLHttpRequest|GearsHttpRequest)
Returns
A new XMLHttpRequest instance.
Returns
Options describing how xhr objects obtained from this - factory should be used.

Override this method in subclasses to preserve the caching offered by +goog.net.XmlHttpFactory

Class goog.net.XmlHttpFactory

code »

Abstract base class for an XmlHttpRequest factory.

Constructor

goog.net.XmlHttpFactory ( )
Show:

Instance Methods

Returns
A new XhrLike instance.
Returns
Options describing how xhr objects obtained from this + factory should be used.

Override this method in subclasses to preserve the caching offered by getOptions().

Returns
Options describing how xhr objects obtained from this - factory should be used.

Instance Properties

Cache of options - we only actually call internalGetOptions once.

\ No newline at end of file + factory should be used.

Instance Properties

Cache of options - we only actually call internalGetOptions once.

\ No newline at end of file diff --git a/docs/class_goog_structs_Map.html b/docs/class_goog_structs_Map.html index b3b0b7b..68fa850 100644 --- a/docs/class_goog_structs_Map.html +++ b/docs/class_goog_structs_Map.html @@ -1,21 +1,21 @@ -goog.structs.Map

Class goog.structs.Map

code »

Class for Hash Map datastructure.

Constructor

goog.structs.Map ( opt_map, var_args )
Parameters
opt_map: *=
Map or Object to initialize the map with.
var_args: ...*
If 2 or more arguments are present then they - will be used as key-value pairs.
Show:

Instance Methods

Returns an iterator that iterates over the values or the keys in the map. +goog.structs.Map

Class goog.structs.Map.<K, V>

code »

Class for Hash Map datastructure.

Constructor

goog.structs.Map ( opt_map, var_args )
Parameters
opt_map: *=
Map or Object to initialize the map with.
var_args: ...*
If 2 or more arguments are present then they + will be used as key-value pairs.
Show:

Instance Methods

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.

Parameters
opt_keys: boolean=
True to iterate over the keys. False to iterate - over the values. The default value is false.
Returns
An iterator over the values or keys in the map.

Adds multiple key-value pairs from another goog.structs.Map or Object.

Parameters
map: Object
Object containing the data to add.

Cleans up the temp keys array by removing entries that are no longer in the - map.

Removes all key-value pairs from the map.

Clones a map and returns a new map.

Returns
A new map with the same key-value pairs.

Whether the map contains the given key.

Parameters
key: *
The key to check for.
Returns
Whether the map contains the key.

Whether the map contains the given value. This is O(n).

Parameters
val: *
The value to check for.
Returns
Whether the map contains the value.
code »equals ( otherMap, opt_equalityFn )boolean

Whether this map is equal to the argument map.

Parameters
otherMap: goog.structs.Map
The map against which to test equality.
opt_equalityFn: function(?, ?): boolean=
Optional equality function + over the values. The default value is false.
Returns
An iterator over the values or keys in the map.

Adds multiple key-value pairs from another goog.structs.Map or Object.

Parameters
map: Object
Object containing the data to add.

Cleans up the temp keys array by removing entries that are no longer in the + map.

Removes all key-value pairs from the map.

Clones a map and returns a new map.

Returns
A new map with the same key-value pairs.

Whether the map contains the given key.

Parameters
key: *
The key to check for.
Returns
Whether the map contains the key.

Whether the map contains the given value. This is O(n).

Parameters
val: V
The value to check for.
Returns
Whether the map contains the value.
code »equals ( otherMap, opt_equalityFn )boolean

Whether this map is equal to the argument map.

Parameters
otherMap: goog.structs.Map
The map against which to test equality.
opt_equalityFn: function(V, V): boolean=
Optional equality function to test equality of values. If not specified, this will test whether - the values contained in each map are identical objects.
Returns
Whether the maps are equal.
code »get ( key, opt_val )*

Returns the value for the given key. If the key is not found and the default - value is not given this will return undefined.

Parameters
key: *
The key to get the value for.
opt_val: *=
The value to return if no item is found for the given - key, defaults to undefined.
Returns
The value for the given key.
Returns
The number of key-value pairs in the map.

Returns an iterator that iterates over the keys in the map. Removal of keys - while iterating might have undesired side effects.

Returns
An iterator over the keys in the map.

Returns the keys of the map.

Returns
Array of string values.

Returns an iterator that iterates over the values in the map. Removal of - keys while iterating might have undesired side effects.

Returns
An iterator over the values in the map.

Returns the values of the map.

Returns
The values in the map.
Returns
Whether the map is empty.

Removes a key-value pair based on the key. This is O(logN) amortized due to + the values contained in each map are identical objects.Returns

Whether the maps are equal.
code »<T> forEach ( f, opt_obj )

Calls the given function on each entry in the map.

Parameters
f
opt_obj: T=
The value of "this" inside f.
code »<DEFAULT> get ( key, opt_val )(V|DEFAULT)

Returns the value for the given key. If the key is not found and the default + value is not given this will return undefined.

Parameters
key: *
The key to get the value for.
opt_val: DEFAULT=
The value to return if no item is found for the + given key, defaults to undefined.
Returns
The value for the given key.
Returns
The number of key-value pairs in the map.

Returns an iterator that iterates over the keys in the map. Removal of keys + while iterating might have undesired side effects.

Returns
An iterator over the keys in the map.

Returns the keys of the map.

Returns
Array of string values.

Returns an iterator that iterates over the values in the map. Removal of + keys while iterating might have undesired side effects.

Returns
An iterator over the values in the map.
code »getValues ( )!Array.<V>

Returns the values of the map.

Returns
The values in the map.
Returns
Whether the map is empty.

Removes a key-value pair based on the key. This is O(logN) amortized due to updating the keys array whenever the count becomes half the size of the keys - in the keys array.

Parameters
key: *
The key to remove.
Returns
Whether object was removed.
code »set ( key, value )*

Adds a key-value pair to the map.

Parameters
key: *
The key.
value: *
The value to add.
Returns
Some subclasses return a value.
Returns
Object representation of the map.

Returns a new map in which all the keys and values are interchanged + in the keys array.

Parameters
key: *
The key to remove.
Returns
Whether object was removed.
code »set ( key, value )*

Adds a key-value pair to the map.

Parameters
key: *
The key.
value: V
The value to add.
Returns
Some subclasses return a value.
Returns
Object representation of the map.

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)}.

Returns
The transposed map.

Instance Properties

The number of key value pairs in the map.

An array of keys. This is necessary for two reasons: + It acts very similarly to {goog.object.transpose(Object)}.

Returns
The transposed map.

Instance Properties

The number of key value pairs in the map.

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 @@ -24,5 +24,5 @@ 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).

Underlying JS object used to implement the map.

Version used to detect changes while iterating.

Static Functions

Default equality test for values.

Parameters
a: *
The first value.
b: *
The second value.
Returns
Whether a and b reference the same object.

Safe way to test for hasOwnProperty. It even allows testing for - 'hasOwnProperty'.

Parameters
obj: Object
The object to test for presence of the given key.
key: *
The key to check for.
Returns
Whether the object has the key.
\ No newline at end of file + memory allocation in IE).

Underlying JS object used to implement the map.

Version used to detect changes while iterating.

Static Functions

Default equality test for values.

Parameters
a: *
The first value.
b: *
The second value.
Returns
Whether a and b reference the same object.

Safe way to test for hasOwnProperty. It even allows testing for + 'hasOwnProperty'.

Parameters
obj: Object
The object to test for presence of the given key.
key: *
The key to check for.
Returns
Whether the object has the key.
\ No newline at end of file diff --git a/docs/class_goog_structs_Set.html b/docs/class_goog_structs_Set.html new file mode 100644 index 0000000..306ff30 --- /dev/null +++ b/docs/class_goog_structs_Set.html @@ -0,0 +1,28 @@ +goog.structs.Set

Class goog.structs.Set.<T>

code »
All implemented interfaces:
goog.structs.Collection.<(T|null)>

A set that can contain both primitives and objects. Adding and removing + elements is O(1). Primitives are treated as identical if they have the same + type and convert to the same string. Objects are treated as identical only + if they are references to the same object. WARNING: A goog.structs.Set can + contain both 1 and (new Number(1)), because they are not the same. WARNING: + Adding (new Number(1)) twice will yield two distinct elements, because they + are two different objects. WARNING: Any object that is added to a + goog.structs.Set will be modified! Because goog.getUid() is used to + identify objects, every object in the set will be mutated.

Constructor

goog.structs.Set ( opt_values )
Parameters
opt_values: (Array.<T>|Object)=
Initial values to start with.
Show:

Instance Methods

Returns an iterator that iterates over the elements in this set.

Parameters
opt_keys: boolean=
This argument is ignored.
Returns
An iterator over the elements in this set.
code »add ( element )

Add a primitive or an object to the set.

Parameters
element: T
The primitive or object to add.

Adds all the values in the given collection to this set.

Parameters
col: (Array.<T>|goog.structs.Collection.<T>|Object)
A collection + containing the elements to add.

Removes all elements from this set.

Creates a shallow clone of this set.

Returns
A new set containing all the same elements as + this set.
code »contains ( element )boolean

Tests whether this set contains the given element.

Parameters
element: T
The primitive or object to test for.
Returns
True if this set contains the given element.

Tests whether this set contains all the values in a given collection. + Repeated elements in the collection are ignored, e.g. (new + goog.structs.Set([1, 2])).containsAll([1, 1]) is True.

Parameters
col: (goog.structs.Collection.<T>|Object)
A collection-like object.
Returns
True if the set contains all elements.

Finds all values that are present in this set and not in the given + collection.

Parameters
col: (Array.<T>|goog.structs.Collection.<T>|Object)
A collection.
Returns
A new set containing all the values + (primitives or objects) present in this set but not in the given + collection.

Tests whether the given collection consists of the same elements as this set, + regardless of order, without repetition. Primitives are treated as equal if + they have the same type and convert to the same string; objects are treated + as equal if they are references to the same object. This operation is O(n).

Parameters
col: (goog.structs.Collection.<T>|Object)
A collection.
Returns
True if the given collection consists of the same elements + as this set, regardless of order, without repetition.
Returns
The number of elements in the set.
code »getValues ( )!Array.<T>

Returns an array containing all the elements in this set.

Returns
An array containing all the elements in this set.

Finds all values that are present in both this set and the given collection.

Parameters
col: (Array.<S>|Object)
A collection.
Returns
A new set containing all the values + (primitives or objects) present in both this set and the given + collection.

Tests whether this set is empty.

Returns
True if there are no elements in this set.

Tests whether the given collection contains all the elements in this set. + Primitives are treated as equal if they have the same type and convert to the + same string; objects are treated as equal if they are references to the same + object. This operation is O(n).

Parameters
col: (goog.structs.Collection.<T>|Object)
A collection.
Returns
True if this set is a subset of the given collection.
code »remove ( element )boolean

Removes the given element from this set.

Parameters
element: T
The primitive or object to remove.
Returns
Whether the element was found and removed.

Removes all values in the given collection from this set.

Parameters
col: (Array.<T>|goog.structs.Collection.<T>|Object)
A collection + containing the elements to remove.

Instance Properties

Class for Hash Map datastructure.

Static Functions

Obtains a unique key for an element of the set. Primitives will yield the + same key if they have the same type and convert to the same string. Object + references will yield the same key only if they refer to the same object.

Parameters
val: *
Object or primitive value to get a key for.
Returns
A unique key for this value/object.
\ No newline at end of file diff --git a/docs/class_goog_testing_AsyncTestCase.html b/docs/class_goog_testing_AsyncTestCase.html new file mode 100644 index 0000000..22a3b14 --- /dev/null +++ b/docs/class_goog_testing_AsyncTestCase.html @@ -0,0 +1,81 @@ +goog.testing.AsyncTestCase

Class goog.testing.AsyncTestCase

code »
goog.testing.TestCase
+  └ goog.testing.AsyncTestCase

A test case that is capable of running tests the contain asynchronous logic.

Constructor

goog.testing.AsyncTestCase ( opt_name )
Parameters
opt_name: string=
A descriptive name for the test case.

Classes

goog.testing.AsyncTestCase.ControlBreakingException
An exception class used solely for control flow.
Show:

Type Definitions

code »goog.testing.AsyncTestCase.TopStackFuncResult_ : {controlBreakingExceptionThrown: boolean, message: string}
Represents result of top stack function call.

Instance Methods

Defined in goog.testing.AsyncTestCase

Calls the given function, redirecting any exceptions to doAsyncError.

Parameters
func: Function
The function to call.
Returns
Returns a + TopStackFuncResult_.

Continue with the next step in the test cycle.

Starts the tests.

code »dbgLog_ ( message )

Logs the given debug message to the console (when enabled).

Parameters
message: string
The message to log.

Handles an exception thrown by a test.

Parameters
opt_e: *=
The exception object associated with the failure + or a string.
Throws
throws a ControlBreakingException.

Calls the tearDown function, catching any errors, and then moves on to + the next step in the testing cycle.

Step 3: Call test.execute().

Step 1: Move to the next test.

Step 5: Call doSuccess()

Sets up the test page and then waits untill the test case has been marked + as ready before executing the tests.

Step 2: Call setUp().

Step 4: Call tearDown().

Wraps doAsyncError() for when we are sure that the test runner has no user + code above it in the stack.

Parameters
opt_e: (string|Error)=
The exception object associated with the + failure or a string.

Enables verbose logging of what is happening inside of the AsyncTestCase.

Ends the current test step and queues the next test step to run.

Finalizes the test case, called when the tests have finished executing.

The current step name.

Returns
Step name.

Replaces the asserts.js assert_() and fail() functions with a wrappers to + catch the exceptions.

Sets a window.onerror handler for catching exceptions that happen in async + callbacks. Note that as of Safari 3.1, Safari does not support this.

code »pump_ ( opt_doFirst )

Calls the next callback when the isReady_ flag is true.

Parameters
opt_doFirst: Function=
A function to call before pumping.
Throws
a ControlBreakingException if there were any failing steps.

Sets up the test page and then waits until the test case has been marked + as ready before executing the tests.

code »setNextStep_ ( func, name )

Sets the next function to call in our sequence of async callbacks.

Parameters
func: Function
The function that executes the next step.
name: string
A description of the next step.

Signals once to continue with the test. If this is the last signal that the + test was waiting on, call continueTesting.

Enables the timeout timer. This timer fires unless continueTesting is + called.

Disables the timeout timer.

Unhooks window.onerror and _assert.

code »waitForAsync ( opt_name )

Informs the testcase not to continue to the next step in the test cycle + until continueTesting is called.

Parameters
opt_name: string=
A description of what we are waiting for.
code »waitForSignals ( times, opt_name )

Informs the testcase not to continue to the next step in the test cycle + until signal is called the specified number of times. Within a test, this + function behaves additively if called multiple times; the number of signals + to wait for will be the sum of all expected number of signals this function + was called with.

Parameters
times: number
The number of signals to receive before + continuing testing.
opt_name: string=
A description of what we are waiting for.

Defined in goog.testing.TestCase

code »add ( test )

Adds a new test to the test case.

Parameters
test: goog.testing.TestCase.Test
The test to add.
code »addNewTest ( name, ref, opt_scope )

Creates and adds a new test. + + Convenience function to make syntax less awkward when not using automatic + test discovery.

Parameters
name: string
The test name.
ref: !Function
Reference to the test function.
opt_scope: !Object=
Optional scope that the test function should be + called in.

Adds any functions defined in the global scope that correspond to + lifecycle events for the test case. Overrides setUp, tearDown, setUpPage, + tearDownPage and runTests if they are defined.

Adds any functions defined in the global scope that are prefixed with "test" + to the test case.

Clears a timeout created by this.timeout().

Parameters
id: number
A timeout id.

Counts the number of files that were loaded for dependencies that are + required to run the test.

Returns
The number of files loaded.

Creates a goog.testing.TestCase.Test from an auto-discovered + function.

Parameters
name: string
The name of the function.
ref: function(): void
The auto-discovered function.
Returns
The newly created test.
code »doError ( test, opt_e )

Handles a test that failed.

Parameters
test: goog.testing.TestCase.Test
The test that failed.
opt_e: *=
The exception object associated with the + failure or a string.

Handles a test that passed.

Parameters
test: goog.testing.TestCase.Test
The test that passed.

Executes each of the tests.

Returns the number of tests actually run in the test case, i.e. subtracting + any which are skipped.

Returns
The number of un-ignored tests.
Returns
The function name prefix used to auto-discover tests.
Returns
Time since the last batch of tests was started.

Returns the number of tests contained in the test case.

Returns
The number of tests.
code »getGlobals ( opt_prefix )!Array

Gets list of objects that potentially contain test cases. For IE 8 and below, + this is the global "this" (for properties set directly on the global this or + window) and the RuntimeObject (for global variables and functions). For all + other browsers, the array simply contains the global this.

Parameters
opt_prefix: string=
An optional prefix. If specified, only get things + under this prefix. Note that the prefix is only honored in IE, since it + supports the RuntimeObject: + http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx + TODO: Remove this option.
Returns
A list of objects that should be inspected.
Returns
The name of the test.

Returns the number of script files that were loaded in order to run the test.

Returns
The number of script files.
code »getReport ( opt_verbose )string

Returns a string detailing the results from the test.

Parameters
opt_verbose: boolean=
If true results will include data about all + tests, not just what failed.
Returns
The results from the test.

Returns the amount of time it took for the test to run.

Returns
The run time, in milliseconds.

Returns the test results object: a map from test names to a list of test + failures (if any exist).

Returns
Tests results object.

Gets the tests.

Returns
The test array.

Returns the current time.

Returns
HH:MM:SS.
Returns
Whether the test case is running inside the multi test + runner.
Returns
Whether the test was a success.
code »log ( val )

Logs an object to the console, if available.

Parameters
val: *
The value to log. Will be ToString'd.
Parameters
name: string
Failed test name.
opt_e: *=
The exception object associated with the + failure or a string.
Returns
Error object.

Checks to see if the test should be marked as failed before it is run. + + If there was an error in setUpPage, we treat that as a failure for all tests + and mark them all as having failed.

Parameters
testCase: goog.testing.TestCase.Test
The current test case.
Returns
Whether the test was marked as failed.

Returns the current test and increments the pointer.

Returns
The current test case.
Returns
The current time in milliseconds, don't use goog.now as some + tests override it.

Reorders the tests depending on the order field.

Parameters
tests: Array.<goog.testing.TestCase.Test>
An array of tests to + reorder.
code »pad_ ( number )string

Pads a number to make it have a leading zero if it's less than 10.

Parameters
number: number
The number to pad.
Returns
The resulting string.

Resets the test case pointer, so that next returns the first test.

code »saveMessage ( message )

Saves a message to the result set.

Parameters
message: string
The message to save.
code »setBatchTime ( batchTime )
Parameters
batchTime: number
Time since the last batch of tests was started.

Sets the callback function that should be executed when the tests have + completed.

Parameters
fn: Function
The callback function.
code »setTests ( tests )

Sets the tests.

Parameters
tests: !Array.<goog.testing.TestCase.Test>
A new test array.

Gets called before every goog.testing.TestCase.Test is been executed. Can be + overridden to add set up functionality to each test.

Gets called before any tests are executed. Can be overridden to set up the + environment for the whole test case.

Can be overridden in test classes to indicate whether the tests in a case + should be run in that particular situation. For example, this could be used + to stop tests running in a particular browser, where browser support for + the class under test was absent.

Returns
Whether any of the tests in the case should be run.

Gets called after every goog.testing.TestCase.Test has been executed. Can be + overriden to add tear down functionality to each test.

Gets called after all tests have been executed. Can be overridden to tear + down the entire test case.

code »timeout ( fn, time )number

Calls a function after a delay, using the protected timeout.

Parameters
fn: Function
The function to call.
time: number
Delay in milliseconds.
Returns
The timeout id.

Trims a path to be only that after google3.

Parameters
path: string
The path to trim.
Returns
The resulting string.

Instance Properties

Defined in goog.testing.AsyncTestCase

Marks if the cleanUp() function has been called for the currently running + test.

The stage of the test we are currently on.

The name of the stage of the test we are currently on.

Turn on extra logging to help debug failing async. tests.

Number of signals to wait for before continuing testing when waitForSignals + is used.

A flag to prevent recursive exception handling.

Flag used to determine if we can move to the next step in the testing loop.

The stage of the test we should run next.

The name of the stage of the test we should run next.

The number of times we have thrown a ControlBreakingException so that we + know not to complain in our window.onerror handler. In Webkit, window.onerror + is not supported, and so this counter will keep going up but we won't care + about it.

A reference to the original window.onerror function.

Number of signals received.

Flag that tells us if there is a function in the call stack that will make + a call to pump_().

How long to wait for a single step of a test to complete in milliseconds. + A step starts when a call to waitForAsync() is made.

How long to wait after a failed test before moving onto the next one. + The purpose of this is to allow any pending async callbacks from the failing + test to finish up and not cause the next test to fail.

The handle to the current setTimeout timer.

Defined in goog.testing.TestCase

Time since the last batch of tests was started, if batchTime exceeds + #maxRunTime a timeout will be used to stop the tests blocking the + browser and a new batch will be started.

Pointer to the current test.

Exception object that was detected before a test runs.

A name for the test case.

Optional callback that will be executed when the test has finalized.

The order to run the auto-discovered tests in.

Object used to encapsulate the test results.

Whether the test case is running.

Timestamp for when the test was started.

Whether the test case has ever tried to execute.

Set of test names and/or indices to execute, or null if all tests should + be executed. + + Indices are included to allow automation tools to run a subset of the + tests without knowing the exact contents of the test file. + + Indices should only be used with SORTED ordering. + + Example valid values: +

    +
  • [testName] +
  • [testName1, testName2] +
  • [2] - will run the 3rd test in the order specified +
  • [1,3,5] +
  • [testName1, testName2, 3, 5] - will work +

    Array of test functions that can be executed.

    Static Functions

    Preferred way of creating an AsyncTestCase. Creates one and initializes it + with the G_testRunner.

    Parameters
    opt_name: string=
    A descriptive name for the test case.
    Returns
    The created AsyncTestCase.

    Static Properties

    \ No newline at end of file diff --git a/docs/class_goog_testing_AsyncTestCase_ControlBreakingException.html b/docs/class_goog_testing_AsyncTestCase_ControlBreakingException.html new file mode 100644 index 0000000..1336ae2 --- /dev/null +++ b/docs/class_goog_testing_AsyncTestCase_ControlBreakingException.html @@ -0,0 +1 @@ +goog.testing.AsyncTestCase.ControlBreakingException

    Class goog.testing.AsyncTestCase.ControlBreakingException

    code »

    An exception class used solely for control flow.

    Constructor

    goog.testing.AsyncTestCase.ControlBreakingException ( opt_message )
    Parameters
    opt_message: string=
    Error message.
    Show:

    Instance Methods

    code »toString ( )string

    Instance Properties

    Marks this object as a ControlBreakingException

    The exception message.

    Static Properties

    \ No newline at end of file diff --git a/docs/class_goog_testing_FunctionCall.html b/docs/class_goog_testing_FunctionCall.html new file mode 100644 index 0000000..1b6b6e8 --- /dev/null +++ b/docs/class_goog_testing_FunctionCall.html @@ -0,0 +1,2 @@ +goog.testing.FunctionCall

    Class goog.testing.FunctionCall

    code »

    Struct for a single function call.

    Constructor

    goog.testing.FunctionCall ( func, thisContext, args, ret, error )
    Parameters
    func: !Function
    The called function.
    thisContext: !Object
    this context of called function.
    args: !Arguments
    Arguments of the called function.
    ret: *
    Return value of the function or undefined in case of error.
    error: *
    The error thrown by the function or null if none.
    Show:

    Instance Methods

    code »getArgument ( index )*

    Returns the nth argument of the called function.

    Parameters
    index: number
    0-based index of the argument.
    Returns
    The argument value or undefined if there is no such argument.
    Returns
    Arguments of the called function.
    code »getError ( )*
    Returns
    The error thrown by the function or null if none.
    Returns
    The called function.
    Returns
    Return value of the function or undefined in case of error.
    Returns
    this context of called function. It is the same as + the created object if the function is a constructor.

    Instance Properties

    \ No newline at end of file diff --git a/docs/class_goog_testing_JsUnitException.html b/docs/class_goog_testing_JsUnitException.html new file mode 100644 index 0000000..2b0f8ae --- /dev/null +++ b/docs/class_goog_testing_JsUnitException.html @@ -0,0 +1,2 @@ +goog.testing.JsUnitException

    Class goog.testing.JsUnitException

    code »
    Error
    +  └ goog.testing.JsUnitException

    Constructor

    goog.testing.JsUnitException ( comment, opt_message )
    Parameters
    comment: string
    A summary for the exception.
    opt_message: ?string=
    A description of the exception.
    Show:

    Instance Methods

    Defined in goog.testing.JsUnitException

    code »toString ( )string

    Instance Properties

    Defined in goog.testing.JsUnitException

    code »comment : (null|string)

    Static Properties

    \ No newline at end of file diff --git a/docs/class_goog_testing_LooseExpectationCollection.html b/docs/class_goog_testing_LooseExpectationCollection.html new file mode 100644 index 0000000..cf6bd52 --- /dev/null +++ b/docs/class_goog_testing_LooseExpectationCollection.html @@ -0,0 +1,4 @@ +goog.testing.LooseExpectationCollection

    Class goog.testing.LooseExpectationCollection

    code »

    This class is an ordered collection of expectations for one method. Since + the loose mock does most of its verification at the time of $verify, this + class is necessary to manage the return/throw behavior when the mock is + being called.

    Constructor

    goog.testing.LooseExpectationCollection ( )
    Show:

    Instance Methods

    code »addExpectation ( expectation )

    Adds an expectation to this collection.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation to add.

    Gets the list of expectations in this collection.

    Returns
    The array of expectations.

    Instance Properties

    The list of expectations. All of these should have the same name.

    \ No newline at end of file diff --git a/docs/class_goog_testing_LooseMock.html b/docs/class_goog_testing_LooseMock.html new file mode 100644 index 0000000..c08bb54 --- /dev/null +++ b/docs/class_goog_testing_LooseMock.html @@ -0,0 +1,25 @@ +goog.testing.LooseMock

    Class goog.testing.LooseMock

    code »
    goog.testing.Mock
    +  └ goog.testing.LooseMock
    All implemented interfaces:
    goog.testing.MockInterface

    This is a mock that does not care about the order of method calls. As a + result, it won't throw exceptions until verify() is called. The only + exception is that if a method is called that has no expectations, then an + exception will be thrown.

    Constructor

    goog.testing.LooseMock ( objectToMock, opt_ignoreUnexpectedCalls, opt_mockStaticMethods, opt_createProxy )
    Parameters
    objectToMock: (Object|Function)
    The object that should be mocked, or + the constructor of an object to mock.
    opt_ignoreUnexpectedCalls: boolean=
    Whether to ignore unexpected + calls.
    opt_mockStaticMethods: boolean=
    An optional argument denoting that + a mock should be constructed from the static functions of a class.
    opt_createProxy: boolean=
    An optional argument denoting that + a proxy for the target mock should be created.
    Show:

    Instance Methods

    Defined in goog.testing.LooseMock

    code »$recordCall ( name, args )*
    Parameters
    name
    args

    A setter for the ignoreUnexpectedCalls field.

    Parameters
    ignoreUnexpectedCalls: boolean
    Whether to ignore unexpected calls.
    Returns
    This mock object.

    Defined in goog.testing.Mock

    Allows the expectation to be called any number of times.

    Returns
    This mock object.

    Render the provided argument array to a string to help + clients with debugging tests.

    Parameters
    args: ?Array
    The arguments passed to the mock.
    Returns
    Human-readable string.

    Allows the expectation to be called any number of times, as long as it's + called once.

    Returns
    This mock object.

    Allows the expectation to be called 0 or 1 times.

    Returns
    This mock object.
    code »$do ( expectation, args )*

    If this expectation defines a function to be called, + it will be called and its result will be returned. + Otherwise, if the expectation expects to throw, it will throw. + Otherwise, this method will return defined value.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation.
    args: Array
    The arguments to the method.
    Returns
    The return value expected by the mock.

    Specifies a function to call for currently pending expectation. + Note, that using this method overrides declarations made + using $returns() and $throws() methods.

    Parameters
    func: Function
    The function to call.
    Returns
    This mock object.

    Initializes the functions on the mock object.

    Parameters
    objectToMock: Object
    The object being mocked.
    code »$maybeThrow ( expectation )

    If the expectation expects to throw, this method will throw.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation.
    code »$mockMethod ( name )*

    The function that replaces all methods on the mock object.

    Parameters
    name: string
    The name of the method being mocked.
    Returns
    In record mode, returns the mock object. In replay mode, returns + whatever the creator of the mock set as the return value.

    Disallows the expectation from being called.

    Returns
    This mock object.

    Allows the expectation to be called exactly once.

    Returns
    This mock object.

    Throws an exception and records that an exception was thrown.

    Parameters
    ex: Object
    Exception.
    Throws
    Object
    #ex.

    Registers a verfifier function to use when verifying method argument lists.

    Parameters
    methodName: string
    The name of the method for which the verifierFn + should be used.
    fn: Function
    Argument list verifier function. Should take 2 argument + arrays as arguments, and return true if they are considered equivalent.
    Returns
    This mock object.

    Specifies a return value for the currently pending expectation.

    Parameters
    val: *
    The return value.
    Returns
    This mock object.
    code »$throwCallException ( name, args, opt_expectation )

    Throw an exception based on an incorrect method call.

    Parameters
    name: string
    Name of method called.
    args: ?Array
    Arguments passed to the mock.
    opt_expectation: goog.testing.MockExpectation=
    Expected next call, + if any.
    code »$throwException ( comment, opt_message )

    Throws an exception and records that an exception was thrown.

    Parameters
    comment: string
    A short comment about the exception.
    opt_message: ?string=
    A longer message about the exception.
    Throws
    Object
    JsUnitException object.

    Specifies a value for the currently pending expectation to throw.

    Parameters
    val: *
    The value to throw.
    Returns
    This mock object.

    Specifies the number of times the expectation should be called.

    Parameters
    times: number
    The number of times this method will be called.
    Returns
    This mock object.
    code »$verifyCall ( expectation, name, args )boolean

    Verifies that a method call matches an expectation.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation to check.
    name: string
    The name of the called method.
    args: ?Array
    The arguments passed to the mock.
    Returns
    Whether the call matches the expectation.

    Instance Properties

    Defined in goog.testing.LooseMock

    The calls that have been made; we cache them to verify at the end. Each + element is an array where the first element is the name, and the second + element is the arguments.

    A map of method names to a LooseExpectationCollection for that method.

    Whether to ignore unexpected calls.

    Defined in goog.testing.Mock

    Map of argument name to optional argument list verifier function.

    The expectation currently being created. All methods that modify the + current expectation return the Mock object for easy chaining, so this is + where we keep track of the expectation that's currently being modified.

    A proxy for the mock. This can be used for dependency injection in lieu of + the mock if the test requires a strict instanceof check.

    Whether or not we are in recording mode.

    First exception thrown by this mock; used in $verify.

    Static Properties

    \ No newline at end of file diff --git a/docs/class_goog_testing_Mock.html b/docs/class_goog_testing_Mock.html new file mode 100644 index 0000000..6cfe20d --- /dev/null +++ b/docs/class_goog_testing_Mock.html @@ -0,0 +1,29 @@ +goog.testing.Mock

    Class goog.testing.Mock

    code »
    All implemented interfaces:
    goog.testing.MockInterface

    The base class for a mock object.

    Constructor

    goog.testing.Mock ( objectToMock, opt_mockStaticMethods, opt_createProxy )
    Parameters
    objectToMock: (Object|Function)
    The object that should be mocked, or + the constructor of an object to mock.
    opt_mockStaticMethods: boolean=
    An optional argument denoting that + a mock should be constructed from the static functions of a class.
    opt_createProxy: boolean=
    An optional argument denoting that + a proxy for the target mock should be created.
    Show:

    Instance Methods

    Allows the expectation to be called any number of times.

    Returns
    This mock object.

    Render the provided argument array to a string to help + clients with debugging tests.

    Parameters
    args: ?Array
    The arguments passed to the mock.
    Returns
    Human-readable string.

    Allows the expectation to be called any number of times, as long as it's + called once.

    Returns
    This mock object.

    Allows the expectation to be called 0 or 1 times.

    Returns
    This mock object.
    code »$do ( expectation, args )*

    If this expectation defines a function to be called, + it will be called and its result will be returned. + Otherwise, if the expectation expects to throw, it will throw. + Otherwise, this method will return defined value.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation.
    args: Array
    The arguments to the method.
    Returns
    The return value expected by the mock.

    Specifies a function to call for currently pending expectation. + Note, that using this method overrides declarations made + using $returns() and $throws() methods.

    Parameters
    func: Function
    The function to call.
    Returns
    This mock object.

    Initializes the functions on the mock object.

    Parameters
    objectToMock: Object
    The object being mocked.
    code »$maybeThrow ( expectation )

    If the expectation expects to throw, this method will throw.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation.
    code »$mockMethod ( name )*

    The function that replaces all methods on the mock object.

    Parameters
    name: string
    The name of the method being mocked.
    Returns
    In record mode, returns the mock object. In replay mode, returns + whatever the creator of the mock set as the return value.

    Disallows the expectation from being called.

    Returns
    This mock object.

    Allows the expectation to be called exactly once.

    Returns
    This mock object.

    Throws an exception and records that an exception was thrown.

    Parameters
    ex: Object
    Exception.
    Throws
    Object
    #ex.
    code »$recordCall ( name, args )*

    Records an actual method call, intended to be overridden by a + subclass. The subclass must find the pending expectation and return the + correct value.

    Parameters
    name: string
    The name of the method being called.
    args: Array
    The arguments to the method.
    Returns
    The return expected by the mock.

    Records the currently pending expectation, intended to be overridden by a + subclass.

    Registers a verfifier function to use when verifying method argument lists.

    Parameters
    methodName: string
    The name of the method for which the verifierFn + should be used.
    fn: Function
    Argument list verifier function. Should take 2 argument + arrays as arguments, and return true if they are considered equivalent.
    Returns
    This mock object.

    Switches from recording to replay mode.

    Resets the state of this mock object. This clears all pending expectations + without verifying, and puts the mock in recording mode.

    Specifies a return value for the currently pending expectation.

    Parameters
    val: *
    The return value.
    Returns
    This mock object.
    code »$throwCallException ( name, args, opt_expectation )

    Throw an exception based on an incorrect method call.

    Parameters
    name: string
    Name of method called.
    args: ?Array
    Arguments passed to the mock.
    opt_expectation: goog.testing.MockExpectation=
    Expected next call, + if any.
    code »$throwException ( comment, opt_message )

    Throws an exception and records that an exception was thrown.

    Parameters
    comment: string
    A short comment about the exception.
    opt_message: ?string=
    A longer message about the exception.
    Throws
    Object
    JsUnitException object.

    Specifies a value for the currently pending expectation to throw.

    Parameters
    val: *
    The value to throw.
    Returns
    This mock object.

    Specifies the number of times the expectation should be called.

    Parameters
    times: number
    The number of times this method will be called.
    Returns
    This mock object.

    Verify that all of the expectations were met. Should be overridden by + subclasses.

    code »$verifyCall ( expectation, name, args )boolean

    Verifies that a method call matches an expectation.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation to check.
    name: string
    The name of the called method.
    args: ?Array
    The arguments passed to the mock.
    Returns
    Whether the call matches the expectation.

    Instance Properties

    Map of argument name to optional argument list verifier function.

    The expectation currently being created. All methods that modify the + current expectation return the Mock object for easy chaining, so this is + where we keep track of the expectation that's currently being modified.

    A proxy for the mock. This can be used for dependency injection in lieu of + the mock if the test requires a strict instanceof check.

    Whether or not we are in recording mode.

    First exception thrown by this mock; used in $verify.

    Static Properties

    Option that may be passed when constructing function, method, and + constructor mocks. Indicates that the expected calls should be accepted in + any order.

    This array contains the name of the functions that are part of the base + Object prototype. + Basically a copy of goog.object.PROTOTYPE_FIELDS_.

    Option that may be passed when constructing function, method, and + constructor mocks. Indicates that the expected calls should be accepted in + the recorded order only.

    \ No newline at end of file diff --git a/docs/class_goog_testing_MockClock.html b/docs/class_goog_testing_MockClock.html new file mode 100644 index 0000000..894cd55 --- /dev/null +++ b/docs/class_goog_testing_MockClock.html @@ -0,0 +1,66 @@ +goog.testing.MockClock

    Class goog.testing.MockClock

    code »
    goog.Disposable
    +  └ goog.testing.MockClock
    All implemented interfaces:
    goog.disposable.IDisposable

    Class for unit testing code that uses setTimeout and clearTimeout. + + NOTE: If you are using MockClock to test code that makes use of + goog.fx.Animation, then you must either: + + 1. Install and dispose of the MockClock in setUpPage() and tearDownPage() + respectively (rather than setUp()/tearDown()). + + or + + 2. Ensure that every test clears the animation queue by calling + mockClock.tick(x) at the end of each test function (where `x` is large + enough to complete all animations). + + Otherwise, if any animation is left pending at the time that + MockClock.dispose() is called, that will permanently prevent any future + animations from playing on the page.

    Constructor

    goog.testing.MockClock ( opt_autoInstall )
    Parameters
    opt_autoInstall: boolean=
    Install the MockClock at construction time.
    Show:

    Instance Methods

    Defined in goog.testing.MockClock

    Clears a requestAnimationFrame. + Mock implementation for cancelRequestAnimationFrame.

    Parameters
    timeoutKey: number
    The requestAnimationFrame key to clear.
    code »clearInterval_ ( timeoutKey )

    Clears an interval. + Mock implementation for clearInterval.

    Parameters
    timeoutKey: number
    The interval key to clear.
    code »clearTimeout_ ( timeoutKey )

    Clears a timeout. + Mock implementation for clearTimeout.

    Parameters
    timeoutKey: number
    The timeout key to clear.

    Signals that the mock clock has been reset, allowing objects that + maintain their own internal state to reset.

    Returns
    The MockClock's current time in milliseconds.
    Returns
    delay The amount of time between when a timeout is + scheduled to fire and when it actually fires, in milliseconds. May + be negative.
    Returns
    The number of timeouts that have been scheduled.

    Installs the MockClock by overriding the global object's implementation of + setTimeout, setInterval, clearTimeout and clearInterval.

    code »isTimeoutSet ( timeoutKey )boolean
    Parameters
    timeoutKey: number
    The timeout key.
    Returns
    Whether the timer has been set and not cleared, + independent of the timeout's expiration. In other words, the timeout + could have passed or could be scheduled for the future. Either way, + this function returns true or false depending only on whether the + provided timeoutKey represents a timeout that has been set and not + cleared.

    Installs the mocks for requestAnimationFrame and cancelRequestAnimationFrame.

    Schedules a function to be called when an animation frame is triggered. + Mock implementation for requestAnimationFrame.

    Parameters
    funcToCall: Function
    The function to call.
    Returns
    The number of timeouts created.

    Resets the MockClock, removing all timeouts that are scheduled and resets + the fake timer count.

    Runs any function that is scheduled before a certain time. Timeouts can + be made to fire early or late if timeoutDelay_ is non-0.

    Parameters
    endTime: number
    The latest time in the range, in milliseconds.
    code »scheduleFunction_ ( timeoutKey, funcToCall, millis, recurring )

    Schedules a function to be run at a certain time.

    Parameters
    timeoutKey: number
    The timeout key.
    funcToCall: Function
    The function to call.
    millis: number
    The number of milliseconds to call it in.
    recurring: boolean
    Whether to function call should recur.
    code »setImmediate_ ( funcToCall )number

    Schedules a function to be called immediately after the current JS + execution. + Mock implementation for setImmediate.

    Parameters
    funcToCall: Function
    The function to call.
    Returns
    The number of timeouts created.
    code »setInterval_ ( funcToCall, millis )number

    Schedules a function to be called every millis milliseconds. + Mock implementation for setInterval.

    Parameters
    funcToCall: Function
    The function to call.
    millis: number
    The number of milliseconds between calls.
    Returns
    The number of timeouts created.

    Sets the amount of time between when a timeout is scheduled to fire and when + it actually fires.

    Parameters
    delay: number
    The delay in milliseconds. May be negative.
    code »setTimeout_ ( funcToCall, millis )number

    Schedules a function to be called after millis milliseconds. + Mock implementation for setTimeout.

    Parameters
    funcToCall: Function
    The function to call.
    millis: number
    The number of milliseconds to call it after.
    Returns
    The number of timeouts created.
    code »tick ( opt_millis )number

    Increments the MockClock's time by a given number of milliseconds, running + any functions that are now overdue.

    Parameters
    opt_millis: number=
    Number of milliseconds to increment the counter. + If not specified, clock ticks 1 millisecond.
    Returns
    Current mock time in milliseconds.

    Removes the MockClock's hooks into the global object's functions and revert + to their original values.

    Defined in goog.Disposable

    code »<T> addOnDisposeCallback ( callback, opt_scope )

    Invokes a callback function when this object is disposed. Callbacks are + invoked in the order in which they were added.

    Parameters
    callback: function(this: T): ?
    The callback function.
    opt_scope: T=
    An optional scope to call the callback in.
    code »dispose ( )void

    Disposes of the object. If the object hasn't already been disposed of, calls + #disposeInternal. Classes that extend goog.Disposable should + override #disposeInternal in order to delete references to COM + objects, DOM nodes, and other disposable objects. Reentrant.

    Returns
    Nothing.
    Deprecated: Use #isDisposed instead.
    Returns
    Whether the object has been disposed of.
    Returns
    Whether the object has been disposed of.

    Associates a disposable object with this object so that they will be disposed + together.

    Parameters
    disposable: goog.disposable.IDisposable
    that will be disposed when + this object is disposed.

    Instance Properties

    Defined in goog.testing.MockClock

    Map of deleted keys. These keys represents keys that were deleted in a + clearInterval, timeoutid -> object.

    The current simulated time in milliseconds.

    Reverse-order queue of timers to fire. + + The last item of the queue is popped off. Insertion happens from the + right. For example, the expiration times for each element of the queue + might be in the order 300, 200, 200.

    PropertyReplacer instance which overwrites and resets setTimeout, + setInterval, etc. or null if the MockClock is not installed.

    Additional delay between the time a timeout was set to fire, and the time + it actually fires. Useful for testing workarounds for this Firefox 2 bug: + https://bugzilla.mozilla.org/show_bug.cgi?id=291386 + May be negative.

    Count of the number of timeouts made.

    Defined in goog.Disposable

    If monitoring the goog.Disposable instances is enabled, stores the creation + stack trace of the Disposable instance.

    Whether the object has been disposed of.

    Callbacks to invoke when this object is disposed.

    Static Functions

    Inserts a timer descriptor into a descending-order queue. + + Later-inserted duplicates appear at lower indices. For example, the + asterisk in (5,4,*,3,2,1) would be the insertion point for 3.

    Parameters
    timeout: Object
    The timeout to insert, with numerical runAtMillis + property.
    queue: Array.<Object>
    The queue to insert into, with each element + having a numerical runAtMillis property.

    Static Properties

    Maximum 32-bit signed integer. + + Timeouts over this time return immediately in many browsers, due to integer + overflow. Such known browsers include Firefox, Chrome, and Safari, but not + IE.

    Default wait timeout for mocking requestAnimationFrame (in milliseconds).

    \ No newline at end of file diff --git a/docs/class_goog_testing_MockControl.html b/docs/class_goog_testing_MockControl.html new file mode 100644 index 0000000..661a96c --- /dev/null +++ b/docs/class_goog_testing_MockControl.html @@ -0,0 +1,22 @@ +goog.testing.MockControl

    Class goog.testing.MockControl

    code »

    Controls a set of mocks. Controlled mocks are replayed, verified, and + cleaned-up at the same time.

    Constructor

    goog.testing.MockControl ( )
    Show:

    Instance Methods

    Calls replay on each controlled mock.

    Calls reset on each controlled mock.

    Calls tearDown on each controlled mock, if necesssary.

    Calls verify on each controlled mock.

    Takes control of this mock.

    Parameters
    mock: goog.testing.MockInterface
    Mock to be controlled.
    Returns
    The same mock passed in, + for convenience.
    code »createConstructorMock ( scope, constructorName, opt_strictness )!goog.testing.MockInterface

    Creates a controlled MethodMock for a constructor. Passes its arguments + through to the MethodMock constructor. See + goog.testing.createConstructorMock for details.

    Parameters
    scope: Object
    The scope of the constructor to be mocked out.
    constructorName: string
    The name of the function we're going to mock.
    opt_strictness: number=
    One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
    Returns
    The mocked method.
    code »createFunctionMock ( opt_functionName, opt_strictness )goog.testing.MockInterface

    Creates a controlled FunctionMock. Passes its arguments through to the + FunctionMock constructor.

    Parameters
    opt_functionName: string=
    The optional name of the function to mock + set to '[anonymous mocked function]' if not passed in.
    opt_strictness: number=
    One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
    Returns
    The mocked function.
    code »createGlobalFunctionMock ( functionName, opt_strictness )goog.testing.MockInterface

    Creates a controlled GlobalFunctionMock. Passes its arguments through to the + GlobalFunctionMock constructor.

    Parameters
    functionName: string
    The name of the function we're going to mock.
    opt_strictness: number=
    One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
    Returns
    The mocked function.
    code »createLooseMock ( objectToMock, opt_ignoreUnexpectedCalls, opt_mockStaticMethods, opt_createProxy )!goog.testing.LooseMock

    Creates a controlled LooseMock. Passes its arguments through to the + LooseMock constructor.

    Parameters
    objectToMock: (Object|Function)
    The object that should be mocked, or + the constructor of an object to mock.
    opt_ignoreUnexpectedCalls: boolean=
    Whether to ignore unexpected + calls.
    opt_mockStaticMethods: boolean=
    An optional argument denoting that + a mock should be constructed from the static functions of a class.
    opt_createProxy: boolean=
    An optional argument denoting that + a proxy for the target mock should be created.
    Returns
    The mock object.
    code »createMethodMock ( scope, functionName, opt_strictness )!goog.testing.MockInterface

    Creates a controlled MethodMock. Passes its arguments through to the + MethodMock constructor.

    Parameters
    scope: Object
    The scope of the method to be mocked out.
    functionName: string
    The name of the function we're going to mock.
    opt_strictness: number=
    One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
    Returns
    The mocked method.
    code »createStrictMock ( objectToMock, opt_mockStaticMethods, opt_createProxy )!goog.testing.StrictMock

    Creates a controlled StrictMock. Passes its arguments through to the + StrictMock constructor.

    Parameters
    objectToMock: (Object|Function)
    The object that should be mocked, or + the constructor of an object to mock.
    opt_mockStaticMethods: boolean=
    An optional argument denoting that + a mock should be constructed from the static functions of a class.
    opt_createProxy: boolean=
    An optional argument denoting that + a proxy for the target mock should be created.
    Returns
    The mock object.

    Instance Properties

    The list of mocks being controlled.

    \ No newline at end of file diff --git a/docs/class_goog_testing_MockExpectation.html b/docs/class_goog_testing_MockExpectation.html new file mode 100644 index 0000000..2619180 --- /dev/null +++ b/docs/class_goog_testing_MockExpectation.html @@ -0,0 +1,3 @@ +goog.testing.MockExpectation

    Class goog.testing.MockExpectation

    code »

    This is a class that represents an expectation.

    Constructor

    goog.testing.MockExpectation ( name )
    Parameters
    name: string
    The name of the method for this expectation.
    Show:

    Instance Methods

    Allow expectation failures to include messages.

    Parameters
    message: string
    The failure message.

    Get the error messages seen so far.

    Returns
    Error messages separated by \n.

    Get how many error messages have been seen so far.

    Returns
    Count of error messages.

    Instance Properties

    The number of times this method is called by real code.

    The arguments that are expected to be passed to this function

    An array of error messages for expectations not met.

    The value that will be thrown when the method is called

    The maximum number of times this method should be called.

    The minimum number of times this method should be called.

    The name of the method that is expected to be called.

    The value that this method should return.

    The function which will be executed when this method is called. + Method arguments will be passed to this function, and return value + of this function will be returned by the method.

    The number of times this method is called during the verification phase.

    \ No newline at end of file diff --git a/docs/class_goog_testing_ObjectPropertyString.html b/docs/class_goog_testing_ObjectPropertyString.html new file mode 100644 index 0000000..4195cc7 --- /dev/null +++ b/docs/class_goog_testing_ObjectPropertyString.html @@ -0,0 +1,3 @@ +goog.testing.ObjectPropertyString

    Class goog.testing.ObjectPropertyString

    code »

    Object to pass a property name as a string literal and its containing object + when the JSCompiler is rewriting these names. This should only be used in + test code.

    Constructor

    goog.testing.ObjectPropertyString ( object, propertyString )
    Parameters
    object: Object
    The containing object.
    propertyString: (Object|string)
    Property name as a string literal.
    Show:

    Instance Methods

    Returns
    The object.
    Returns
    The property string.

    Instance Properties

    \ No newline at end of file diff --git a/docs/class_goog_testing_PropertyReplacer.html b/docs/class_goog_testing_PropertyReplacer.html new file mode 100644 index 0000000..14dd913 --- /dev/null +++ b/docs/class_goog_testing_PropertyReplacer.html @@ -0,0 +1,46 @@ +goog.testing.PropertyReplacer

    Class goog.testing.PropertyReplacer

    code »

    Helper class for stubbing out variables and object properties for unit tests. + This class can change the value of some variables before running the test + cases, and to reset them in the tearDown phase. + See googletest.StubOutForTesting as an analogy in Python: + http://protobuf.googlecode.com/svn/trunk/python/stubout.py + + Example usage: +

    var stubs = new goog.testing.PropertyReplacer();
    +
    + function setUp() {
    +   // Mock functions used in all test cases.
    +   stubs.set(Math, 'random', function() {
    +     return 4;  // Chosen by fair dice roll. Guaranteed to be random.
    +   });
    + }
    +
    + function tearDown() {
    +   stubs.reset();
    + }
    +
    + function testThreeDice() {
    +   // Mock a constant used only in this test case.
    +   stubs.set(goog.global, 'DICE_COUNT', 3);
    +   assertEquals(12, rollAllDice());
    + }
    + + Constraints on altered objects: +
      +
    • DOM subclasses aren't supported. +
    • The value of the objects' constructor property must either be equal to + the real constructor or kept untouched. +

    Constructor

    goog.testing.PropertyReplacer ( )
    Show:

    Instance Methods

    code »remove ( obj, key )

    Deletes the key from the object while saving its original value.

    Parameters
    obj: (Object|Function)
    The JavaScript or native object or function to + alter. See the constraints in the class description.
    key: string
    The key to delete.
    code »replace ( obj, key, value )

    Changes an existing value in an object to another one of the same type while + saving its original state. The advantage of replace over #set + is that replace protects against typos and erroneously passing tests + after some members have been renamed during a refactoring.

    Parameters
    obj: (Object|Function)
    The JavaScript or native object or function to + alter. See the constraints in the class description.
    key: string
    The key to change the value for. It has to be present + either in obj or in its prototype chain.
    value: *
    The new value to set. It has to have the same type as the + original value. The types are compared with goog.typeOf.
    Throws
    Error
    In case of missing key or type mismatch.

    Resets all changes made by goog.testing.PropertyReplacer.prototype.set.

    code »set ( obj, key, value )

    Adds or changes a value in an object while saving its original state.

    Parameters
    obj: (Object|Function)
    The JavaScript or native object or function to + alter. See the constraints in the class description.
    key: string
    The key to change the value for.
    value: *
    The new value to set.
    code »setPath ( path, value )

    Builds an object structure for the provided namespace path. Doesn't + overwrite those prefixes of the path that are already objects or functions.

    Parameters
    path: string
    The path to create or alter, e.g. 'goog.ui.Menu'.
    value: *
    The value to set.

    Instance Properties

    Stores the values changed by the set() method in chronological order. + Its items are objects with 3 fields: 'object', 'key', 'value'. The + original value for the given key in the given object is stored under the + 'value' key.

    Static Functions

    Deletes a key from an object. Sets it to undefined or empty string if the + delete failed.

    Parameters
    obj: (Object|Function)
    The object or function to delete a key from.
    key: string
    The key to delete.

    Tells if the given key exists in the object. Ignores inherited fields.

    Parameters
    obj: (Object|Function)
    The JavaScript or native object or function + whose key is to be checked.
    key: string
    The key to check.
    Returns
    Whether the object has the key as own key.

    Static Properties

    Indicates that a key didn't exist before having been set by the set() method.

    \ No newline at end of file diff --git a/docs/class_goog_testing_StrictMock.html b/docs/class_goog_testing_StrictMock.html new file mode 100644 index 0000000..aae2814 --- /dev/null +++ b/docs/class_goog_testing_StrictMock.html @@ -0,0 +1,23 @@ +goog.testing.StrictMock

    Class goog.testing.StrictMock

    code »
    goog.testing.Mock
    +  └ goog.testing.StrictMock
    All implemented interfaces:
    goog.testing.MockInterface

    This is a mock that verifies that methods are called in the order that they + are specified during the recording phase. Since it verifies order, it + follows 'fail fast' semantics. If it detects a deviation from the + expectations, it will throw an exception and not wait for verify to be + called.

    Constructor

    goog.testing.StrictMock ( objectToMock, opt_mockStaticMethods, opt_createProxy )
    Parameters
    objectToMock: (Object|Function)
    The object that should be mocked, or + the constructor of an object to mock.
    opt_mockStaticMethods: boolean=
    An optional argument denoting that + a mock should be constructed from the static functions of a class.
    opt_createProxy: boolean=
    An optional argument denoting that + a proxy for the target mock should be created.
    Show:

    Instance Methods

    Defined in goog.testing.StrictMock

    code »$recordCall ( name, args )*
    Parameters
    name
    args

    Defined in goog.testing.Mock

    Allows the expectation to be called any number of times.

    Returns
    This mock object.

    Render the provided argument array to a string to help + clients with debugging tests.

    Parameters
    args: ?Array
    The arguments passed to the mock.
    Returns
    Human-readable string.

    Allows the expectation to be called any number of times, as long as it's + called once.

    Returns
    This mock object.

    Allows the expectation to be called 0 or 1 times.

    Returns
    This mock object.
    code »$do ( expectation, args )*

    If this expectation defines a function to be called, + it will be called and its result will be returned. + Otherwise, if the expectation expects to throw, it will throw. + Otherwise, this method will return defined value.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation.
    args: Array
    The arguments to the method.
    Returns
    The return value expected by the mock.

    Specifies a function to call for currently pending expectation. + Note, that using this method overrides declarations made + using $returns() and $throws() methods.

    Parameters
    func: Function
    The function to call.
    Returns
    This mock object.

    Initializes the functions on the mock object.

    Parameters
    objectToMock: Object
    The object being mocked.
    code »$maybeThrow ( expectation )

    If the expectation expects to throw, this method will throw.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation.
    code »$mockMethod ( name )*

    The function that replaces all methods on the mock object.

    Parameters
    name: string
    The name of the method being mocked.
    Returns
    In record mode, returns the mock object. In replay mode, returns + whatever the creator of the mock set as the return value.

    Disallows the expectation from being called.

    Returns
    This mock object.

    Allows the expectation to be called exactly once.

    Returns
    This mock object.

    Throws an exception and records that an exception was thrown.

    Parameters
    ex: Object
    Exception.
    Throws
    Object
    #ex.

    Registers a verfifier function to use when verifying method argument lists.

    Parameters
    methodName: string
    The name of the method for which the verifierFn + should be used.
    fn: Function
    Argument list verifier function. Should take 2 argument + arrays as arguments, and return true if they are considered equivalent.
    Returns
    This mock object.

    Switches from recording to replay mode.

    Specifies a return value for the currently pending expectation.

    Parameters
    val: *
    The return value.
    Returns
    This mock object.
    code »$throwCallException ( name, args, opt_expectation )

    Throw an exception based on an incorrect method call.

    Parameters
    name: string
    Name of method called.
    args: ?Array
    Arguments passed to the mock.
    opt_expectation: goog.testing.MockExpectation=
    Expected next call, + if any.
    code »$throwException ( comment, opt_message )

    Throws an exception and records that an exception was thrown.

    Parameters
    comment: string
    A short comment about the exception.
    opt_message: ?string=
    A longer message about the exception.
    Throws
    Object
    JsUnitException object.

    Specifies a value for the currently pending expectation to throw.

    Parameters
    val: *
    The value to throw.
    Returns
    This mock object.

    Specifies the number of times the expectation should be called.

    Parameters
    times: number
    The number of times this method will be called.
    Returns
    This mock object.
    code »$verifyCall ( expectation, name, args )boolean

    Verifies that a method call matches an expectation.

    Parameters
    expectation: goog.testing.MockExpectation
    The expectation to check.
    name: string
    The name of the called method.
    args: ?Array
    The arguments passed to the mock.
    Returns
    Whether the call matches the expectation.

    Instance Properties

    Defined in goog.testing.StrictMock

    Defined in goog.testing.Mock

    Map of argument name to optional argument list verifier function.

    The expectation currently being created. All methods that modify the + current expectation return the Mock object for easy chaining, so this is + where we keep track of the expectation that's currently being modified.

    A proxy for the mock. This can be used for dependency injection in lieu of + the mock if the test requires a strict instanceof check.

    Whether or not we are in recording mode.

    First exception thrown by this mock; used in $verify.

    Static Properties

    \ No newline at end of file diff --git a/docs/class_goog_testing_TestCase.html b/docs/class_goog_testing_TestCase.html new file mode 100644 index 0000000..cd35ab8 --- /dev/null +++ b/docs/class_goog_testing_TestCase.html @@ -0,0 +1,85 @@ +goog.testing.TestCase

    Class goog.testing.TestCase

    code »

    A class representing a JsUnit test case. A TestCase is made up of a number + of test functions which can be run. Individual test cases can override the + following functions to set up their test environment: + - runTests - completely override the test's runner + - setUpPage - called before any of the test functions are run + - tearDownPage - called after all tests are finished + - setUp - called before each of the test functions + - tearDown - called after each of the test functions + - shouldRunTests - called before a test run, all tests are skipped if it + returns false. Can be used to disable tests on browsers + where they aren't expected to pass. + + Use #autoDiscoverLifecycle and #autoDiscoverTests

    Constructor

    goog.testing.TestCase ( opt_name )
    Parameters
    opt_name: string=
    The name of the test case, defaults to + 'Untitled Test Case'.

    Classes

    goog.testing.TestCase.Error
    A class representing an error thrown by the test
    goog.testing.TestCase.Result
    A class for representing test results.
    goog.testing.TestCase.Test
    A class representing a single test function.
    goog.testing.TestCase.protectedDate_
    No Description.

    Enumerations

    goog.testing.TestCase.Order
    The order to run the auto-discovered tests.
    Show:

    Instance Methods

    code »add ( test )

    Adds a new test to the test case.

    Parameters
    test: goog.testing.TestCase.Test
    The test to add.
    code »addNewTest ( name, ref, opt_scope )

    Creates and adds a new test. + + Convenience function to make syntax less awkward when not using automatic + test discovery.

    Parameters
    name: string
    The test name.
    ref: !Function
    Reference to the test function.
    opt_scope: !Object=
    Optional scope that the test function should be + called in.

    Adds any functions defined in the global scope that correspond to + lifecycle events for the test case. Overrides setUp, tearDown, setUpPage, + tearDownPage and runTests if they are defined.

    Adds any functions defined in the global scope that are prefixed with "test" + to the test case.

    Clears a timeout created by this.timeout().

    Parameters
    id: number
    A timeout id.

    Counts the number of files that were loaded for dependencies that are + required to run the test.

    Returns
    The number of files loaded.

    Creates a goog.testing.TestCase.Test from an auto-discovered + function.

    Parameters
    name: string
    The name of the function.
    ref: function(): void
    The auto-discovered function.
    Returns
    The newly created test.

    Cycles through the tests, breaking out using a setTimeout if the execution + time has execeeded #maxRunTime.

    code »doError ( test, opt_e )

    Handles a test that failed.

    Parameters
    test: goog.testing.TestCase.Test
    The test that failed.
    opt_e: *=
    The exception object associated with the + failure or a string.

    Handles a test that passed.

    Parameters
    test: goog.testing.TestCase.Test
    The test that passed.

    Executes each of the tests.

    Finalizes the test case, called when the tests have finished executing.

    Returns the number of tests actually run in the test case, i.e. subtracting + any which are skipped.

    Returns
    The number of un-ignored tests.
    Returns
    The function name prefix used to auto-discover tests.
    Returns
    Time since the last batch of tests was started.

    Returns the number of tests contained in the test case.

    Returns
    The number of tests.
    code »getGlobals ( opt_prefix )!Array

    Gets list of objects that potentially contain test cases. For IE 8 and below, + this is the global "this" (for properties set directly on the global this or + window) and the RuntimeObject (for global variables and functions). For all + other browsers, the array simply contains the global this.

    Parameters
    opt_prefix: string=
    An optional prefix. If specified, only get things + under this prefix. Note that the prefix is only honored in IE, since it + supports the RuntimeObject: + http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx + TODO: Remove this option.
    Returns
    A list of objects that should be inspected.
    Returns
    The name of the test.

    Returns the number of script files that were loaded in order to run the test.

    Returns
    The number of script files.
    code »getReport ( opt_verbose )string

    Returns a string detailing the results from the test.

    Parameters
    opt_verbose: boolean=
    If true results will include data about all + tests, not just what failed.
    Returns
    The results from the test.

    Returns the amount of time it took for the test to run.

    Returns
    The run time, in milliseconds.

    Returns the test results object: a map from test names to a list of test + failures (if any exist).

    Returns
    Tests results object.

    Gets the tests.

    Returns
    The test array.

    Returns the current time.

    Returns
    HH:MM:SS.
    Returns
    Whether the test case is running inside the multi test + runner.
    Returns
    Whether the test was a success.
    code »log ( val )

    Logs an object to the console, if available.

    Parameters
    val: *
    The value to log. Will be ToString'd.
    Parameters
    name: string
    Failed test name.
    opt_e: *=
    The exception object associated with the + failure or a string.
    Returns
    Error object.

    Checks to see if the test should be marked as failed before it is run. + + If there was an error in setUpPage, we treat that as a failure for all tests + and mark them all as having failed.

    Parameters
    testCase: goog.testing.TestCase.Test
    The current test case.
    Returns
    Whether the test was marked as failed.

    Returns the current test and increments the pointer.

    Returns
    The current test case.
    Returns
    The current time in milliseconds, don't use goog.now as some + tests override it.

    Reorders the tests depending on the order field.

    Parameters
    tests: Array.<goog.testing.TestCase.Test>
    An array of tests to + reorder.
    code »pad_ ( number )string

    Pads a number to make it have a leading zero if it's less than 10.

    Parameters
    number: number
    The number to pad.
    Returns
    The resulting string.

    Resets the test case pointer, so that next returns the first test.

    Executes each of the tests. + Overridable by the individual test case. This allows test cases to defer + when the test is actually started. If overridden, finalize must be called + by the test to indicate it has finished.

    code »saveMessage ( message )

    Saves a message to the result set.

    Parameters
    message: string
    The message to save.
    code »setBatchTime ( batchTime )
    Parameters
    batchTime: number
    Time since the last batch of tests was started.

    Sets the callback function that should be executed when the tests have + completed.

    Parameters
    fn: Function
    The callback function.
    code »setTests ( tests )

    Sets the tests.

    Parameters
    tests: !Array.<goog.testing.TestCase.Test>
    A new test array.

    Gets called before every goog.testing.TestCase.Test is been executed. Can be + overridden to add set up functionality to each test.

    Gets called before any tests are executed. Can be overridden to set up the + environment for the whole test case.

    Can be overridden in test classes to indicate whether the tests in a case + should be run in that particular situation. For example, this could be used + to stop tests running in a particular browser, where browser support for + the class under test was absent.

    Returns
    Whether any of the tests in the case should be run.

    Gets called after every goog.testing.TestCase.Test has been executed. Can be + overriden to add tear down functionality to each test.

    Gets called after all tests have been executed. Can be overridden to tear + down the entire test case.

    code »timeout ( fn, time )number

    Calls a function after a delay, using the protected timeout.

    Parameters
    fn: Function
    The function to call.
    time: number
    Delay in milliseconds.
    Returns
    The timeout id.

    Trims a path to be only that after google3.

    Parameters
    path: string
    The path to trim.
    Returns
    The resulting string.

    Instance Properties

    Time since the last batch of tests was started, if batchTime exceeds + #maxRunTime a timeout will be used to stop the tests blocking the + browser and a new batch will be started.

    Pointer to the current test.

    Exception object that was detected before a test runs.

    A name for the test case.

    Optional callback that will be executed when the test has finalized.

    The order to run the auto-discovered tests in.

    Object used to encapsulate the test results.

    Whether the test case is running.

    Timestamp for when the test was started.

    Whether the test case has ever tried to execute.

    Set of test names and/or indices to execute, or null if all tests should + be executed. + + Indices are included to allow automation tools to run a subset of the + tests without knowing the exact contents of the test file. + + Indices should only be used with SORTED ordering. + + Example valid values: +

      +
    • [testName] +
    • [testName1, testName2] +
    • [2] - will run the 3rd test in the order specified +
    • [1,3,5] +
    • [testName1, testName2, 3, 5] - will work +

      Array of test functions that can be executed.

      Static Functions

      Gets list of objects that potentially contain test cases. For IE 8 and below, + this is the global "this" (for properties set directly on the global this or + window) and the RuntimeObject (for global variables and functions). For all + other browsers, the array simply contains the global this.

      Parameters
      opt_prefix: string=
      An optional prefix. If specified, only get things + under this prefix. Note that the prefix is only honored in IE, since it + supports the RuntimeObject: + http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx + TODO: Remove this option.
      Returns
      A list of objects that should be inspected.

      Initializes the given test case with the global test runner 'G_testRunner'.

      Parameters
      testCase: goog.testing.TestCase
      The test case to install.

      Save a reference to window.clearTimeout, so any code that overrides + the default behavior (e.g. MockClock) doesn't affect our runner.

      Save a reference to window.setTimeout, so any code that overrides the + default behavior (the MockClock, for example) doesn't affect our runner.

      Static Properties

      Avoid a dependency on goog.userAgent and keep our own reference of whether + the browser is IE.

      TODO(user) replace this with prototype.currentTest. + Name of the current test that is running, or null if none is running.

      The maximum amount of time that the test can run before we force it to be + async. This prevents the test runner from blocking the browser and + potentially hurting the Selenium test harness.

      Saved string referencing goog.global.setTimeout's string serialization. IE + sometimes fails to uphold equality for setTimeout, but the string version + stays the same.

      \ No newline at end of file diff --git a/docs/class_goog_testing_TestCase_Error.html b/docs/class_goog_testing_TestCase_Error.html new file mode 100644 index 0000000..dbe77b2 --- /dev/null +++ b/docs/class_goog_testing_TestCase_Error.html @@ -0,0 +1 @@ +goog.testing.TestCase.Error

      Class goog.testing.TestCase.Error

      code »

      A class representing an error thrown by the test

      Constructor

      goog.testing.TestCase.Error ( source, message, opt_stack )
      Parameters
      source: string
      The name of the test which threw the error.
      message: string
      The error message.
      opt_stack: string=
      A string showing the execution stack.
      Show:

      Instance Methods

      Returns a string representing the error object.

      Returns
      A string representation of the error.

      Instance Properties

      Reference to the test function.

      The name of the test which threw the error.

      Scope that the test function should be called in.

      \ No newline at end of file diff --git a/docs/class_goog_testing_TestCase_Result.html b/docs/class_goog_testing_TestCase_Result.html new file mode 100644 index 0000000..094cf67 --- /dev/null +++ b/docs/class_goog_testing_TestCase_Result.html @@ -0,0 +1,5 @@ +goog.testing.TestCase.Result

      Class goog.testing.TestCase.Result

      code »

      A class for representing test results. A bag of public properties.

      Constructor

      goog.testing.TestCase.Result ( testCase )
      Parameters
      testCase: goog.testing.TestCase
      The test case that owns this result.
      Show:

      Instance Methods

      Returns
      A summary of the tests, including total number of tests that + passed, failed, and the time taken.
      Returns
      Whether the test was successful.

      Instance Properties

      Whether the tests have completed.

      Errors encountered while running the test.

      Messages to show the user after running the test.

      The number of files loaded to run this test.

      Test results for each test that was run. The test name is always added + as the key in the map, and the array of strings is an optional list + of failure messages. If the array is empty, the test passed. Otherwise, + the test failed.

      Total number of tests that were actually run.

      The amount of time the tests took to run.

      Number of successful tests.

      The test case that owns this result.

      Whether this test case was suppressed by shouldRunTests() returning false.

      Total number of tests that should have been run.

      \ No newline at end of file diff --git a/docs/class_goog_testing_TestCase_Test.html b/docs/class_goog_testing_TestCase_Test.html new file mode 100644 index 0000000..16aac26 --- /dev/null +++ b/docs/class_goog_testing_TestCase_Test.html @@ -0,0 +1,2 @@ +goog.testing.TestCase.Test

      Class goog.testing.TestCase.Test

      code »

      A class representing a single test function.

      Constructor

      goog.testing.TestCase.Test ( name, ref, opt_scope )
      Parameters
      name: string
      The test name.
      ref: Function
      Reference to the test function.
      opt_scope: Object=
      Optional scope that the test function should be + called in.
      Show:

      Instance Methods

      Executes the test function.

      Instance Properties

      The name of the test.

      Reference to the test function.

      Scope that the test function should be called in.

      \ No newline at end of file diff --git a/docs/class_goog_testing_TestCase_protectedDate_.html b/docs/class_goog_testing_TestCase_protectedDate_.html new file mode 100644 index 0000000..3087ec2 --- /dev/null +++ b/docs/class_goog_testing_TestCase_protectedDate_.html @@ -0,0 +1,3 @@ +goog.testing.TestCase.protectedDate_

      Class goog.testing.TestCase.protectedDate_

      code »

      Save a reference to window.Date, so any code that overrides + the default behavior doesn't affect our runner.

      Constructor

      goog.testing.TestCase.protectedDate_ ( )
      Show:

      Instance Methods

      code »setDate ( dayValue )

      Sets the day of the month for a specified date according to local time.

      Parameters
      dayValue
      code »setFullYear ( yearValue, opt_monthValue, opt_dayValue )

      Sets the full year for a specified date according to local time.

      Parameters
      yearValue
      opt_monthValue
      opt_dayValue
      code »setHours ( hoursValue, opt_minutesValue, opt_secondsValue, opt_msValue )

      Sets the hours for a specified date according to local time.

      Parameters
      hoursValue
      opt_minutesValue
      opt_secondsValue
      opt_msValue
      code »setMilliseconds ( millisecondsValue )

      Sets the milliseconds for a specified date according to local time.

      Parameters
      millisecondsValue
      code »setMinutes ( minutesValue, opt_secondsValue, opt_msValue )

      Sets the minutes for a specified date according to local time.

      Parameters
      minutesValue
      opt_secondsValue
      opt_msValue
      code »setMonth ( monthValue, opt_dayValue )

      Set the month for a specified date according to local time.

      Parameters
      monthValue
      opt_dayValue
      code »setSeconds ( secondsValue, opt_msValue )

      Sets the seconds for a specified date according to local time.

      Parameters
      secondsValue
      opt_msValue
      code »setTime ( timeValue )

      Sets the Date object to the time represented by a number of milliseconds + since January 1, 1970, 00:00:00 UTC.

      Parameters
      timeValue
      code »setUTCDate ( dayValue )

      Sets the day of the month for a specified date according to universal time.

      Parameters
      dayValue
      code »setUTCFullYear ( yearValue, opt_monthValue, opt_dayValue )

      Sets the full year for a specified date according to universal time.

      Parameters
      yearValue
      opt_monthValue
      opt_dayValue
      code »setUTCHours ( hoursValue, opt_minutesValue, opt_secondsValue, opt_msValue )

      Sets the hour for a specified date according to universal time.

      Parameters
      hoursValue
      opt_minutesValue
      opt_secondsValue
      opt_msValue
      code »setUTCMilliseconds ( millisecondsValue )

      Sets the milliseconds for a specified date according to universal time.

      Parameters
      millisecondsValue
      code »setUTCMinutes ( minutesValue, opt_secondsValue, opt_msValue )

      Sets the minutes for a specified date according to universal time.

      Parameters
      minutesValue
      opt_secondsValue
      opt_msValue
      code »setUTCMonth ( monthValue, opt_dayValue )

      Sets the month for a specified date according to universal time.

      Parameters
      monthValue
      opt_dayValue
      code »setUTCSeconds ( secondsValue, opt_msValue )

      Sets the seconds for a specified date according to universal time.

      Parameters
      secondsValue
      opt_msValue
      code »setYear ( yearValue )

      Sets the year for a specified date according to local time.

      Parameters
      yearValue
      code »toJSON ( opt_ignoredKey )string
      Parameters
      opt_ignoredKey
      code »toLocaleDateString ( opt_locales, opt_options )string
      Parameters
      opt_locales
      opt_options
      code »toLocaleFormat ( formatString )string
      Parameters
      formatString
      code »toLocaleTimeString ( opt_locales, opt_options )string
      Parameters
      opt_locales
      opt_options
      \ No newline at end of file diff --git a/docs/class_goog_testing_TestRunner.html b/docs/class_goog_testing_TestRunner.html new file mode 100644 index 0000000..c3cde33 --- /dev/null +++ b/docs/class_goog_testing_TestRunner.html @@ -0,0 +1,17 @@ +goog.testing.TestRunner

      Class goog.testing.TestRunner

      code »

      Construct a test runner. + + NOTE(user): This is currently pretty weird, I'm essentially trying to + create a wrapper that the Selenium test can hook into to query the state of + the running test case, while making goog.testing.TestCase general.

      Constructor

      goog.testing.TestRunner ( )
      Show:

      Instance Methods

      Executes a test case and prints the results to the window.

      Returns the number of script files that were loaded in order to run the test.

      Returns
      The number of script files.
      code »getReport ( opt_verbose )string

      Returns a report of the test case that ran. + Used by Selenium Hooks.

      Parameters
      opt_verbose: boolean=
      If true results will include data about all + tests, not just what failed.
      Returns
      A report summary of the test.

      Returns the amount of time it took for the test to run. + Used by Selenium Hooks.

      Returns
      The run time, in milliseconds.
      Returns
      A map of test names to a list of + test failures (if any) to provide formatted data for the test runner.

      Returns true if the test case runner has errors that were caught outside of + the test case.

      Returns
      Whether there were JS errors.
      code »initialize ( testCase )

      Initializes the test runner.

      Parameters
      testCase: goog.testing.TestCase
      The test case to initialize with.

      Returns true if the test runner is finished. + Used by Selenium Hooks.

      Returns
      Whether the test runner is active.

      Returns true if the test runner is initialized. + Used by Selenium Hooks.

      Returns
      Whether the test runner is active.
      Returns
      Whether the test runner should fail on an empty + test case.

      Returns true if the test case didn't fail. + Used by Selenium Hooks.

      Returns
      Whether the current test returned successfully.

      Logs a message to the current test case.

      Parameters
      s: string
      The text to output to the log.

      Logs an error that occurred. Used in the case of environment setting up + an onerror handler.

      Parameters
      msg: string
      Error message.

      Log failure in current running test.

      Parameters
      ex: Error
      Exception.

      Writes the results to the document when the test case completes.

      Sets a function to use as a filter for errors.

      Parameters
      fn: function(string)
      Filter function.
      code »setStrict ( strict )

      By default, the test runner is strict, and fails if it runs an empty + test case.

      Parameters
      strict: boolean
      Whether the test runner should fail on an empty + test case.

      Writes a nicely formatted log out to the document.

      Parameters
      log: string
      The string to write.

      Instance Properties

      Function to use when filtering errors.

      Errors that occurred in the window.

      Whether the test runner has been initialized yet.

      Element created in the document to add test results to.

      Whether an empty test case counts as an error.

      Reference to the active test case.

      \ No newline at end of file diff --git a/docs/class_goog_testing_events_Event.html b/docs/class_goog_testing_events_Event.html new file mode 100644 index 0000000..4c0df8e --- /dev/null +++ b/docs/class_goog_testing_events_Event.html @@ -0,0 +1,6 @@ +goog.testing.events.Event

      Class goog.testing.events.Event

      code »
      Event
      +  └ goog.testing.events.Event

      goog.events.BrowserEvent expects an Event so we provide one for JSCompiler. + + This clones a lot of the functionality of goog.events.Event. This used to + use a mixin, but the mixin results in confusing the two types when compiled.

      Constructor

      goog.testing.events.Event ( type, opt_target )
      Parameters
      type: string
      Event Type.
      opt_target: Object=
      Reference to the object that is the target of + this event.
      Show:

      Instance Methods

      Defined in goog.testing.events.Event

      Defined in Event

      code »initEvent ( eventTypeArg, canBubbleArg, cancelableArg )undefined
      Parameters
      eventTypeArg
      canBubbleArg
      cancelableArg

      Instance Properties

      Defined in goog.testing.events.Event

      Whether to cancel the event in internal capture/bubble processing for IE.

      Return value for in internal capture/bubble processing for IE.

      \ No newline at end of file diff --git a/docs/class_goog_testing_mockmatchers_ArgumentMatcher.html b/docs/class_goog_testing_mockmatchers_ArgumentMatcher.html new file mode 100644 index 0000000..ffc68fd --- /dev/null +++ b/docs/class_goog_testing_mockmatchers_ArgumentMatcher.html @@ -0,0 +1,9 @@ +goog.testing.mockmatchers.ArgumentMatcher

      Class goog.testing.mockmatchers.ArgumentMatcher

      code »

      A simple interface for executing argument matching. A match in this case is + testing to see if a supplied object fits a given criteria. True is returned + if the given criteria is met.

      Constructor

      goog.testing.mockmatchers.ArgumentMatcher ( opt_matchFn, opt_matchName )
      Parameters
      opt_matchFn: Function=
      A function that evaluates a given argument + and returns true if it meets a given criteria.
      opt_matchName: ?string=
      The name expressing intent as part of + an error message for when a match fails.
      Show:

      Instance Methods

      code »matches ( toVerify, opt_expectation )boolean

      A function that takes a match argument and an optional MockExpectation + which (if provided) will get error information and returns whether or + not it matches.

      Parameters
      toVerify: *
      The argument that should be verified.
      opt_expectation: ?goog.testing.MockExpectation=
      The expectation + for this match.
      Returns
      Whether or not a given argument passes verification.

      Instance Properties

      A function that evaluates a given argument and returns true if it meets a + given criteria.

      A string indicating the match intent (e.g. isBoolean or isString).

      \ No newline at end of file diff --git a/docs/class_goog_testing_mockmatchers_IgnoreArgument.html b/docs/class_goog_testing_mockmatchers_IgnoreArgument.html new file mode 100644 index 0000000..4d325c4 --- /dev/null +++ b/docs/class_goog_testing_mockmatchers_IgnoreArgument.html @@ -0,0 +1,8 @@ +goog.testing.mockmatchers.IgnoreArgument

      Class goog.testing.mockmatchers.IgnoreArgument

      code »
      goog.testing.mockmatchers.ArgumentMatcher
      +  └ goog.testing.mockmatchers.IgnoreArgument

      A matcher that always returns true. It is useful when the user does not care + for some arguments. + For example: mockFunction('username', 'password', IgnoreArgument);

      Constructor

      goog.testing.mockmatchers.IgnoreArgument ( )
      Show:

      Instance Methods

      code »matches ( toVerify, opt_expectation )boolean

      A function that takes a match argument and an optional MockExpectation + which (if provided) will get error information and returns whether or + not it matches.

      Parameters
      toVerify: *
      The argument that should be verified.
      opt_expectation: ?goog.testing.MockExpectation=
      The expectation + for this match.
      Returns
      Whether or not a given argument passes verification.

      Instance Properties

      A function that evaluates a given argument and returns true if it meets a + given criteria.

      A string indicating the match intent (e.g. isBoolean or isString).

      Static Properties

      \ No newline at end of file diff --git a/docs/class_goog_testing_mockmatchers_InstanceOf.html b/docs/class_goog_testing_mockmatchers_InstanceOf.html new file mode 100644 index 0000000..a5f073b --- /dev/null +++ b/docs/class_goog_testing_mockmatchers_InstanceOf.html @@ -0,0 +1,6 @@ +goog.testing.mockmatchers.InstanceOf

      Class goog.testing.mockmatchers.InstanceOf

      code »
      goog.testing.mockmatchers.ArgumentMatcher
      +  └ goog.testing.mockmatchers.InstanceOf

      A matcher that verifies that an argument is an instance of a given class.

      Constructor

      goog.testing.mockmatchers.InstanceOf ( ctor )
      Parameters
      ctor: Function
      The class that will be used for verification.
      Show:

      Instance Methods

      code »matches ( toVerify, opt_expectation )boolean

      A function that takes a match argument and an optional MockExpectation + which (if provided) will get error information and returns whether or + not it matches.

      Parameters
      toVerify: *
      The argument that should be verified.
      opt_expectation: ?goog.testing.MockExpectation=
      The expectation + for this match.
      Returns
      Whether or not a given argument passes verification.

      Instance Properties

      A function that evaluates a given argument and returns true if it meets a + given criteria.

      A string indicating the match intent (e.g. isBoolean or isString).

      Static Properties

      \ No newline at end of file diff --git a/docs/class_goog_testing_mockmatchers_ObjectEquals.html b/docs/class_goog_testing_mockmatchers_ObjectEquals.html new file mode 100644 index 0000000..967af04 --- /dev/null +++ b/docs/class_goog_testing_mockmatchers_ObjectEquals.html @@ -0,0 +1,5 @@ +goog.testing.mockmatchers.ObjectEquals

      Class goog.testing.mockmatchers.ObjectEquals

      code »
      goog.testing.mockmatchers.ArgumentMatcher
      +  └ goog.testing.mockmatchers.ObjectEquals

      A matcher that verifies that the argument is an object that equals the given + expected object, using a deep comparison.

      Constructor

      goog.testing.mockmatchers.ObjectEquals ( expectedObject )
      Parameters
      expectedObject: Object
      An object to match against when + verifying the argument.
      Show:

      Instance Methods

      Defined in goog.testing.mockmatchers.ObjectEquals

      code »matches ( toVerify, opt_expectation )boolean
      Parameters
      toVerify
      opt_expectation

      Instance Properties

      Defined in goog.testing.mockmatchers.ArgumentMatcher

      A function that evaluates a given argument and returns true if it meets a + given criteria.

      A string indicating the match intent (e.g. isBoolean or isString).

      Static Properties

      \ No newline at end of file diff --git a/docs/class_goog_testing_mockmatchers_RegexpMatch.html b/docs/class_goog_testing_mockmatchers_RegexpMatch.html new file mode 100644 index 0000000..641c947 --- /dev/null +++ b/docs/class_goog_testing_mockmatchers_RegexpMatch.html @@ -0,0 +1,6 @@ +goog.testing.mockmatchers.RegexpMatch

      Class goog.testing.mockmatchers.RegexpMatch

      code »
      goog.testing.mockmatchers.ArgumentMatcher
      +  └ goog.testing.mockmatchers.RegexpMatch

      A matcher that verifies that an argument matches a given RegExp.

      Constructor

      goog.testing.mockmatchers.RegexpMatch ( regexp )
      Parameters
      regexp: RegExp
      The regular expression that the argument must match.
      Show:

      Instance Methods

      code »matches ( toVerify, opt_expectation )boolean

      A function that takes a match argument and an optional MockExpectation + which (if provided) will get error information and returns whether or + not it matches.

      Parameters
      toVerify: *
      The argument that should be verified.
      opt_expectation: ?goog.testing.MockExpectation=
      The expectation + for this match.
      Returns
      Whether or not a given argument passes verification.

      Instance Properties

      A function that evaluates a given argument and returns true if it meets a + given criteria.

      A string indicating the match intent (e.g. isBoolean or isString).

      Static Properties

      \ No newline at end of file diff --git a/docs/class_goog_testing_mockmatchers_SaveArgument.html b/docs/class_goog_testing_mockmatchers_SaveArgument.html new file mode 100644 index 0000000..54da271 --- /dev/null +++ b/docs/class_goog_testing_mockmatchers_SaveArgument.html @@ -0,0 +1,8 @@ +goog.testing.mockmatchers.SaveArgument

      Class goog.testing.mockmatchers.SaveArgument

      code »
      goog.testing.mockmatchers.ArgumentMatcher
      +  └ goog.testing.mockmatchers.SaveArgument

      A matcher that saves the argument that it is verifying so that your unit test + can perform extra tests with this argument later. For example, if the + argument is a callback method, the unit test can then later call this + callback to test the asynchronous portion of the call.

      Constructor

      goog.testing.mockmatchers.SaveArgument ( opt_matcher, opt_matchName )
      Parameters
      opt_matcher: (goog.testing.mockmatchers.ArgumentMatcher|Function)=
      Argument matcher or matching function that will be used to validate the + argument. By default, argument will always be valid.
      opt_matchName: ?string=
      The name expressing intent as part of + an error message for when a match fails.
      Show:

      Instance Methods

      Defined in goog.testing.mockmatchers.SaveArgument

      code »matches ( toVerify, opt_expectation )boolean
      Parameters
      toVerify
      opt_expectation

      Instance Properties

      Defined in goog.testing.mockmatchers.SaveArgument

      Saved argument that was verified.

      Delegate match requests to this matcher.

      Defined in goog.testing.mockmatchers.ArgumentMatcher

      A function that evaluates a given argument and returns true if it meets a + given criteria.

      A string indicating the match intent (e.g. isBoolean or isString).

      Static Properties

      \ No newline at end of file diff --git a/docs/class_goog_testing_mockmatchers_TypeOf.html b/docs/class_goog_testing_mockmatchers_TypeOf.html new file mode 100644 index 0000000..8835d33 --- /dev/null +++ b/docs/class_goog_testing_mockmatchers_TypeOf.html @@ -0,0 +1,6 @@ +goog.testing.mockmatchers.TypeOf

      Class goog.testing.mockmatchers.TypeOf

      code »
      goog.testing.mockmatchers.ArgumentMatcher
      +  └ goog.testing.mockmatchers.TypeOf

      A matcher that verifies that an argument is of a given type (e.g. "object").

      Constructor

      goog.testing.mockmatchers.TypeOf ( type )
      Parameters
      type: string
      The type that a given argument must have.
      Show:

      Instance Methods

      code »matches ( toVerify, opt_expectation )boolean

      A function that takes a match argument and an optional MockExpectation + which (if provided) will get error information and returns whether or + not it matches.

      Parameters
      toVerify: *
      The argument that should be verified.
      opt_expectation: ?goog.testing.MockExpectation=
      The expectation + for this match.
      Returns
      Whether or not a given argument passes verification.

      Instance Properties

      A function that evaluates a given argument and returns true if it meets a + given criteria.

      A string indicating the match intent (e.g. isBoolean or isString).

      Static Properties

      \ No newline at end of file diff --git a/docs/class_goog_testing_stacktrace_Frame.html b/docs/class_goog_testing_stacktrace_Frame.html new file mode 100644 index 0000000..460cb26 --- /dev/null +++ b/docs/class_goog_testing_stacktrace_Frame.html @@ -0,0 +1,6 @@ +goog.testing.stacktrace.Frame

      Class goog.testing.stacktrace.Frame

      code »

      Class representing one stack frame.

      Constructor

      goog.testing.stacktrace.Frame ( context, name, alias, args, path )
      Parameters
      context: string
      Context object, empty in case of global functions or + if the browser doesn't provide this information.
      name: string
      Function name, empty in case of anonymous functions.
      alias: string
      Alias of the function if available. For example the + function name will be 'c' and the alias will be 'b' if the function is + defined as a.b = function c() {};.
      args: string
      Arguments of the function in parentheses if available.
      path: string
      File path or URL including line number and optionally + column number separated by colons.
      Show:

      Instance Methods

      Returns
      The function name or empty string if the function is + anonymous and the object field which it's assigned to is unknown.
      Returns
      Whether the stack frame contains an anonymous function.

      Brings one frame of the stack trace into a common format across browsers.

      Returns
      Pretty printed stack frame.

      Instance Properties

      \ No newline at end of file diff --git a/docs/class_webdriver_AbstractBuilder.html b/docs/class_webdriver_AbstractBuilder.html index a8dc544..1b1363e 100644 --- a/docs/class_webdriver_AbstractBuilder.html +++ b/docs/class_webdriver_AbstractBuilder.html @@ -5,10 +5,12 @@
      Defines the remote WebDriver server that should be used for command command execution; may be overridden using webdriver.AbstractBuilder.prototype.usingServer.
      -

      Constructor

      webdriver.AbstractBuilder ( )
      Show:

      Instance Methods

      Builds a new webdriver.WebDriver instance using this builder's +

      Constructor

      webdriver.AbstractBuilder ( )
      Show:

      Instance Methods

      Builds a new webdriver.WebDriver instance using this builder's current configuration.

      Returns
      A new WebDriver client.
      Returns
      The current desired capabilities for this builder.
      Returns
      The URL of the WebDriver server this instance is configured - to use.

      Configures which WebDriver server should be used for new sessions. Overrides + to use.

      Sets the logging preferences for the created session. Preferences may be + changed by repeated calls, or by calling #withCapabilities.

      Parameters
      prefs: !(webdriver.logging.Preferences|Object.<string, string>)
      The + desired logging preferences.
      Returns
      This Builder instance for chain calling.

      Configures which WebDriver server should be used for new sessions. Overrides the value loaded from the webdriver.AbstractBuilder.SERVER_URL_ENV upon creation of this instance.

      Parameters
      url: string
      URL of the server to use.
      Returns
      This Builder instance for chain calling.

      Sets the desired capabilities when requesting a new session. This will overwrite any previously set desired capabilities.

      Parameters
      capabilities: !(Object|webdriver.Capabilities)
      The desired @@ -18,4 +20,4 @@ webdriver.AbstractBuilder#usingServer.

      Static Properties

      The default URL of the WebDriver server to use if webdriver.AbstractBuilder.SERVER_URL_ENV is not set.

      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 #usingServer(url).

      \ No newline at end of file + using #usingServer(url). \ No newline at end of file diff --git a/docs/class_webdriver_ActionSequence.html b/docs/class_webdriver_ActionSequence.html index 86ec373..aaf62c0 100644 --- a/docs/class_webdriver_ActionSequence.html +++ b/docs/class_webdriver_ActionSequence.html @@ -1,87 +1,115 @@ -webdriver.ActionSequence

      Class webdriver.ActionSequence

      code »

      Class for defining sequences of complex user interactions. Each sequence - will not be executed until #perform is called. - -

      Example:

      
      -   new webdriver.ActionSequence(driver).
      -       keyDown(webdriver.Key.SHIFT).
      -       click(element1).
      -       click(element2).
      -       dragAndDrop(element3, element4).
      -       keyUp(webdriver.Key.SHIFT).
      -       perform();
      - 

      Constructor

      webdriver.ActionSequence ( driver )
      Parameters
      driver: !webdriver.WebDriver
      The driver instance to use.
      Show:

      Instance Methods

      code »click ( opt_elementOrButton, opt_button )!webdriver.ActionSequence

      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: -

      sequence.mouseMove(element).click()
      Parameters
      opt_elementOrButton: (webdriver.WebElement|webdriver.Button)=
      Either - the element to interact with or the button to click with. - Defaults to webdriver.Button.LEFT if neither an element nor - button is specified.
      opt_button: webdriver.Button=
      The button to use. Defaults to - webdriver.Button.LEFT. Ignored if a button is provided as the - first argument.
      Returns
      A self reference.
      code »doubleClick ( opt_elementOrButton, opt_button )!webdriver.ActionSequence

      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: -

      sequence.mouseMove(element).doubleClick()
      - -

      Warning: this method currently only supports the left mouse button. See - http://code.google.com/p/selenium/issues/detail?id=4047

      Parameters
      opt_elementOrButton: (webdriver.WebElement|webdriver.Button)=
      Either - the element to interact with or the button to click with. - Defaults to webdriver.Button.LEFT if neither an element nor - button is specified.
      opt_button: webdriver.Button=
      The button to use. Defaults to - webdriver.Button.LEFT. Ignored if a button is provided as the - first argument.
      Returns
      A self reference.
      code »dragAndDrop ( element, location )!webdriver.ActionSequence

      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).

      Parameters
      element: !webdriver.WebElement
      The element to drag.
      location: (!webdriver.WebElement|{x: number, y: number})
      The - location to drag to, either as another WebElement or an offset in pixels.
      Returns
      A self reference.

      Performs a modifier key press. The modifier key is not released - until #keyUp or #sendKeys is called. The key press will be - targetted at the currently focused element.

      Parameters
      key: !webdriver.Key
      The modifier key to push. Must be one of - {ALT, CONTROL, SHIFT, COMMAND, META}.
      Returns
      A self reference.
      Throws
      Error
      If the key is not a valid modifier key.

      Performs a modifier key release. The release is targetted at the currently - focused element.

      Parameters
      key: !webdriver.Key
      The modifier key to release. Must be one of - {ALT, CONTROL, SHIFT, COMMAND, META}.
      Returns
      A self reference.
      Throws
      Error
      If the key is not a valid modifier key.
      code »mouseDown ( opt_elementOrButton, opt_button )!webdriver.ActionSequence

      Presses a mouse button. The mouse button will not be released until - #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. - -

      If an element is provided, the mouse will first be moved to the center - of that element. This is equivalent to: -

      sequence.mouseMove(element).mouseDown()
      - -

      Warning: this method currently only supports the left mouse button. See - http://code.google.com/p/selenium/issues/detail?id=4047

      Parameters
      opt_elementOrButton: (webdriver.WebElement|webdriver.Button)=
      Either - the element to interact with or the button to click with. - Defaults to webdriver.Button.LEFT if neither an element nor - button is specified.
      opt_button: webdriver.Button=
      The button to use. Defaults to - webdriver.Button.LEFT. Ignored if a button is provided as the - first argument.
      Returns
      A self reference.
      code »mouseMove ( location, opt_offset )!webdriver.ActionSequence

      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).

      Parameters
      location: (!webdriver.WebElement|{x: number, y: number})
      The - location to drag to, as either another WebElement or an offset in pixels.
      opt_offset: {x: number, y: number}=
      If the target 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.
      code »mouseUp ( opt_elementOrButton, opt_button )!webdriver.ActionSequence

      Releases a mouse button. Behavior is undefined for calling this function - without a previous call to #mouseDown. - -

      If an element is provided, the mouse will first be moved to the center - of that element. This is equivalent to: -

      sequence.mouseMove(element).mouseUp()
      - -

      Warning: this method currently only supports the left mouse button. See - http://code.google.com/p/selenium/issues/detail?id=4047

      Parameters
      opt_elementOrButton: (webdriver.WebElement|webdriver.Button)=
      Either - the element to interact with or the button to click with. - Defaults to webdriver.Button.LEFT if neither an element nor - button is specified.
      opt_button: webdriver.Button=
      The button to use. Defaults to - webdriver.Button.LEFT. Ignored if a button is provided as the - first argument.
      Returns
      A self reference.

      Executes this action sequence.

      Returns
      A promise that will be resolved once - this sequence has completed.

      Schedules a keyboard action.

      Parameters
      description: string
      A simple descriptive label for the scheduled - action.
      keys: !Array
      The keys to send.
      Returns
      A self reference.
      code »scheduleMouseAction_ ( description, commandName, opt_elementOrButton, opt_button )!webdriver.ActionSequence

      Schedules a mouse action.

      Parameters
      description: string
      A simple descriptive label for the scheduled - action.
      commandName: !webdriver.CommandName
      The name of the command.
      opt_elementOrButton: (webdriver.WebElement|webdriver.Button)=
      Either - the element to interact with or the button to click with. - Defaults to webdriver.Button.LEFT if neither an element nor - button is specified.
      opt_button: webdriver.Button=
      The button to use. Defaults to - webdriver.Button.LEFT. Ignored if the previous argument is - provided as a button.
      Returns
      A self reference.
      code »schedule_ ( description, command )

      Schedules an action to be executed each time #perform is called on - this instance.

      Parameters
      description: string
      A description of the command.
      command: !webdriver.Command
      The command.

      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.

      Parameters
      var_args: ...(string|!webdriver.Key|!Array)
      The keys to type.
      Returns
      A self reference.
      Throws
      Error
      If the key is not a valid modifier key.

      Instance Properties

      Static Functions

      Checks that a key is a modifier key.

      Parameters
      key: !webdriver.Key
      The key to check.
      Throws
      Error
      If the key is not a modifier key.
      \ No newline at end of file +ActionSequence

      class ActionSequence

      Class for defining sequences of complex user interactions. Each sequence +will not be executed until #perform is called.

      +

      Example:

      +
      new webdriver.ActionSequence(driver).
      +    keyDown(webdriver.Key.SHIFT).
      +    click(element1).
      +    click(element2).
      +    dragAndDrop(element3, element4).
      +    keyUp(webdriver.Key.SHIFT).
      +    perform();
      +
      +

      new ActionSequence(driver)

      Parameters
      driverwebdriver.WebDriver

      The driver instance to use.

      +

      Instance Methods

      click(opt_elementOrButton, opt_button)code »

      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:

      +
      sequence.mouseMove(element).click()
      +
      +
      Parameters
      opt_elementOrButton?(webdriver.WebElement|number)=

      Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

      +
      opt_buttonnumber=

      The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +

      doubleClick(opt_elementOrButton, opt_button)code »

      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:

      +
      sequence.mouseMove(element).doubleClick()
      +
      +

      Warning: this method currently only supports the left mouse button. See +issue 4047.

      +
      Parameters
      opt_elementOrButton?(webdriver.WebElement|number)=

      Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

      +
      opt_buttonnumber=

      The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +

      dragAndDrop(element, location)code »

      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).

      +
      Parameters
      elementwebdriver.WebElement

      The element to drag.

      +
      location(webdriver.WebElement|{x: number, y: number})

      The +location to drag to, either as another WebElement or an offset in pixels.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +

      keyDown(key)code »

      Performs a modifier key press. The modifier key is not released +until #keyUp or #sendKeys is called. The key press will be +targetted at the currently focused element.

      +
      Parameters
      keystring

      The modifier key to push. Must be one of +{ALT, CONTROL, SHIFT, COMMAND, META}.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +
      Throws
      Error

      If the key is not a valid modifier key.

      +

      keyUp(key)code »

      Performs a modifier key release. The release is targetted at the currently +focused element.

      +
      Parameters
      keystring

      The modifier key to release. Must be one of +{ALT, CONTROL, SHIFT, COMMAND, META}.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +
      Throws
      Error

      If the key is not a valid modifier key.

      +

      mouseDown(opt_elementOrButton, opt_button)code »

      Presses a mouse button. The mouse button will not be released until +#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.

      +

      If an element is provided, the mouse will first be moved to the center +of that element. This is equivalent to:

      +
      sequence.mouseMove(element).mouseDown()
      +
      +

      Warning: this method currently only supports the left mouse button. See +issue 4047.

      +
      Parameters
      opt_elementOrButton?(webdriver.WebElement|number)=

      Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

      +
      opt_buttonnumber=

      The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +

      mouseMove(location, opt_offset)code »

      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).

      +
      Parameters
      location(webdriver.WebElement|{x: number, y: number})

      The +location to drag to, as either another WebElement or an offset in pixels.

      +
      opt_offset{x: number, y: number}=

      If the target 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
      webdriver.ActionSequence

      A self reference.

      +

      mouseUp(opt_elementOrButton, opt_button)code »

      Releases a mouse button. Behavior is undefined for calling this function +without a previous call to #mouseDown.

      +

      If an element is provided, the mouse will first be moved to the center +of that element. This is equivalent to:

      +
      sequence.mouseMove(element).mouseUp()
      +
      +

      Warning: this method currently only supports the left mouse button. See +issue 4047.

      +
      Parameters
      opt_elementOrButton?(webdriver.WebElement|number)=

      Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

      +
      opt_buttonnumber=

      The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +

      perform()code »

      Executes this action sequence.

      +
      Returns
      webdriver.promise.Promise

      A promise that will be resolved once +this sequence has completed.

      +

      sendKeys(var_args)code »

      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.

      +
      Parameters
      var_args...(string|Array<string>)

      The keys to type.

      +
      Returns
      webdriver.ActionSequence

      A self reference.

      +
      Throws
      Error

      If the key is not a valid modifier key.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_Alert.html b/docs/class_webdriver_Alert.html index d40586f..398446e 100644 --- a/docs/class_webdriver_Alert.html +++ b/docs/class_webdriver_Alert.html @@ -1,109 +1,24 @@ -webdriver.Alert

      Class webdriver.Alert

      code »
      webdriver.promise.Promise
      -  └ webdriver.promise.Deferred
      -      └ webdriver.Alert

      Represents a modal dialog such as alert, 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

      webdriver.Alert ( driver, text )
      Parameters
      driver: !webdriver.WebDriver
      The driver controlling the browser this - alert is attached to.
      text: !(string|webdriver.promise.Promise)
      Either the message text - displayed with this alert, or a promise that will be resolved to said - text.
      Show:

      Instance Methods

      Defined in webdriver.Alert

      Accepts this alert.

      Returns
      A promise that will be resolved when - this command has completed.

      Dismisses this alert.

      Returns
      A promise that will be resolved when - this command has completed.

      Retrieves the message text displayed with this alert. For instance, if the - alert were opened with alert("hello"), then this would return "hello".

      Returns
      A promise that will be resolved to the - text displayed with this alert.

      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).

      Parameters
      text: string
      The text to set.
      Returns
      A promise that will be resolved when - this command has completed.

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will - be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.
      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a - promise and not a reference to this deferred, this instance will wait for - it before resolving.

      Parameters
      opt_value: *=
      The resolved value.
      code »reject ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will - be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise

      code »addBoth ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenFinally() instead.

      Registers a function to be invoked when this promise is either rejected or - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call when this promise is - either resolved or rejected. The function should expect a single - argument: the resolved value or rejection error.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallback ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      Registers a function to be invoked when this promise is successfully - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallbacks ( callback, errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      An alias for webdriver.promise.Promise.prototype.then that permits - the scope of the invoked function to be specified. This function is provided - for backwards compatibility with the Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addErrback ( errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenCatch() instead.

      Registers a function to be invoked when this promise is rejected. - This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »cancel ( reason )

      Cancels the computation of this promise's value, rejecting the promise in the - process.

      Parameters
      reason: *
      The reason this promise is being cancelled. If not an - Error, one will be created using the value's string - representation.
      Returns
      Whether this promise's value is still being computed.
      code »then ( opt_callback, opt_errback )!webdriver.promise.Promise

      Registers listeners for when this instance is resolved. This function most - overridden by subtypes.

      Parameters
      opt_callback: Function=
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_errback: Function=
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      Registers a listener for when this promise is rejected. This is synonymous - with the 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
      errback: !Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      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 finally clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } finally {
      -     cleanUp();
      -   }
      -
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenFinally(cleanUp);
      - 
      - - Note: similar to the 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
      callback

      Instance Properties

      Defined in webdriver.Alert

      Defined in webdriver.promise.Deferred

      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. - -

      This class is based on the Promise/A proposal from CommonJS. Additional - functions are provided for API compatibility with Dojo Deferred objects.

      Static Properties

      \ No newline at end of file +Alert

      class Alert

      Represents a modal dialog such as alert, 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).

      +

      new Alert(driver, text)

      Parameters
      driverwebdriver.WebDriver

      The driver controlling the browser this +alert is attached to.

      +
      textstring

      The message text displayed with this alert.

      +

      Instance Methods

      accept()code »

      Accepts this alert.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when this command has completed.

      +

      dismiss()code »

      Dismisses this alert.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when this command has completed.

      +

      getText()code »

      Retrieves the message text displayed with this alert. For instance, if the +alert were opened with alert("hello"), then this would return "hello".

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved to the text displayed with this alert.

      +

      sendKeys(text)code »

      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).

      +
      Parameters
      textstring

      The text to set.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when this command has completed.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_AlertPromise.html b/docs/class_webdriver_AlertPromise.html new file mode 100644 index 0000000..9abc1b4 --- /dev/null +++ b/docs/class_webdriver_AlertPromise.html @@ -0,0 +1,92 @@ +AlertPromise

      class AlertPromise

      final
      webdriver.Alert
      +  └ webdriver.AlertPromise
      All implemented interfaces
      IThenable<T>
      webdriver.promise.Thenable<webdriver.Alert>

      AlertPromise is a promise that will be fulfilled with an Alert. This promise +serves as a forward proxy on an Alert, allowing calls to be scheduled +directly on this instance before the underlying Alert has been fulfilled. In +other words, the following two statements are equivalent:

      +
      driver.switchTo().alert().dismiss();
      +driver.switchTo().alert().then(function(alert) {
      +  return alert.dismiss();
      +});
      +
      +

      new AlertPromise(driver, alert)

      Parameters
      driverwebdriver.WebDriver

      The driver controlling the browser this +alert is attached to.

      +
      alertwebdriver.promise.Thenable<webdriver.Alert>

      A thenable +that will be fulfilled with the promised alert.

      +

      Instance Methods

      accept()code »

      Defers action until the alert has been located.

      +

      Overrides: webdriver.Alert

      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when this command has completed.

      +

      cancel(opt_reason)code »

      Cancels the computation of this promise's value, rejecting the promise in +the process. This method is a no-op if the promise has already been +resolved.

      +

      Specified by: webdriver.promise.Thenable

      Parameters
      opt_reason?(string|webdriver.promise.CancellationError)=

      The reason this +promise is being cancelled.

      +

      dismiss()code »

      Defers action until the alert has been located.

      +

      Overrides: webdriver.Alert

      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when this command has completed.

      +

      getText()code »

      Defer returning text until the promised alert has been resolved.

      +

      Overrides: webdriver.Alert

      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved to the text displayed with this alert.

      +

      isPending()code »

      Specified by: webdriver.promise.Thenable

      Returns
      boolean

      Whether this promise's value is still being computed.

      +

      sendKeys(text)code »

      Defers action until the alert has been located.

      +

      Overrides: webdriver.Alert

      Parameters
      textstring

      The text to set.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when this command has completed.

      +

      then(opt_callback, opt_errback)code »

      Registers listeners for when this instance is resolved.

      +

      Specified by: webdriver.promise.Thenable, IThenable

      Parameters
      opt_callback?function(T): (R|IThenable<R>)=

      The +function to call if this promise is successfully resolved. The function +should expect a single argument: the promise's resolved value.

      +
      opt_errback?function(*): (R|IThenable<R>)=

      The function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenCatch(errback)code »

      Registers a listener for when this promise is rejected. This is synonymous +with the 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);
      +});
      +
      +

      Specified by: webdriver.promise.Thenable

      Parameters
      errbackfunction(*): (R|IThenable<R>)

      The +function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenFinally(callback)code »

      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 finally clause in a synchronous API:

      +
      // Synchronous API:
      +try {
      +  doSynchronousWork();
      +} finally {
      +  cleanUp();
      +}
      +
      +// Asynchronous promise API:
      +doAsynchronousWork().thenFinally(cleanUp);
      +
      +

      Note: similar to the 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
      +}
      +
      +promise.rejected(Error('one'))
      +    .thenFinally(function() {
      +      throw Error('two');  // Hides Error: one
      +    });
      +
      +

      Specified by: webdriver.promise.Thenable

      Parameters
      callbackfunction(): (R|IThenable<R>)

      The function +to call when this promise is resolved.

      +
      Returns
      webdriver.promise.Promise

      A promise that will be fulfilled +with the callback result.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_Builder.html b/docs/class_webdriver_Builder.html index f650658..a67f28c 100644 --- a/docs/class_webdriver_Builder.html +++ b/docs/class_webdriver_Builder.html @@ -3,7 +3,9 @@ to reuse.

      Configures the builder to create a client that will use an existing WebDriver session.

      Parameters
      id: string
      The existing session ID to use.
      Returns
      This Builder instance for chain calling.

      Defined in webdriver.AbstractBuilder

      Returns
      The current desired capabilities for this builder.
      Returns
      The URL of the WebDriver server this instance is configured - to use.

      Configures which WebDriver server should be used for new sessions. Overrides + to use.

      Sets the logging preferences for the created session. Preferences may be + changed by repeated calls, or by calling #withCapabilities.

      Parameters
      prefs: !(webdriver.logging.Preferences|Object.<string, string>)
      The + desired logging preferences.
      Returns
      This Builder instance for chain calling.

      Configures which WebDriver server should be used for new sessions. Overrides the value loaded from the webdriver.AbstractBuilder.SERVER_URL_ENV upon creation of this instance.

      Parameters
      url: string
      URL of the server to use.
      Returns
      This Builder instance for chain calling.

      Sets the desired capabilities when requesting a new session. This will overwrite any previously set desired capabilities.

      Parameters
      capabilities: !(Object|webdriver.Capabilities)
      The desired @@ -19,4 +21,4 @@ default to creating clients that use this session. To create a new session, use #useExistingSession(boolean). The use of this environment variable requires that webdriver.AbstractBuilder.SERVER_URL_ENV also - be set. \ No newline at end of file + be set. \ No newline at end of file diff --git a/docs/class_webdriver_Capabilities.html b/docs/class_webdriver_Capabilities.html index 12d3834..e301d52 100644 --- a/docs/class_webdriver_Capabilities.html +++ b/docs/class_webdriver_Capabilities.html @@ -1,9 +1,60 @@ -webdriver.Capabilities

      Class webdriver.Capabilities

      code »

      Constructor

      webdriver.Capabilities ( opt_other )
      Parameters
      opt_other: (webdriver.Capabilities|Object)=
      Another set of - capabilities to merge into this instance.
      Show:

      Instance Methods

      code »get ( key )*
      Parameters
      key: string
      The capability to return.
      Returns
      The capability with the given key, or null if it has - not been set.
      code »has ( key )boolean
      Parameters
      key: string
      The capability to check.
      Returns
      Whether the specified capability is set.

      Merges another set of capabilities into this instance. Any duplicates in - the provided set will override those already set on this instance.

      Parameters
      other: !(webdriver.Capabilities|Object)
      The capabilities to - merge into this instance.
      Returns
      A self reference.
      Parameters
      key: string
      The capability to set.
      value: *
      The capability value. Capability values must be JSON - serializable. Pass null to unset the capability.
      Returns
      A self reference.
      Returns
      The JSON representation of this instance.

      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.
      \ No newline at end of file +Capabilities

      class Capabilities

      webdriver.Serializable<Object<string, ?>>
      +  └ webdriver.Capabilities

      new Capabilities(opt_other)

      Parameters
      opt_other?Object=

      Another set of +capabilities to merge into this instance.

      +

      Instance Methods

      get(key)code »

      Parameters
      keystring

      The capability to return.

      +
      Returns

      The capability with the given key, or null if it has +not been set.

      +

      has(key)code »

      Parameters
      keystring

      The capability to check.

      +
      Returns
      boolean

      Whether the specified capability is set.

      +

      merge(other)code »

      Merges another set of capabilities into this instance. Any duplicates in +the provided set will override those already set on this instance.

      +
      Parameters
      otherObject

      The capabilities to +merge into this instance.

      +
      Returns
      webdriver.Capabilities

      A self reference.

      +

      serialize()code »

      Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

      +

      Overrides: webdriver.Serializable

      Returns
      Object<string, ?>

      The JSON representation of this instance. Note, +the returned object may contain nested promises that are promised values.

      +

      set(key, value)code »

      Parameters
      keystring

      The capability to set.

      +
      value*

      The capability value. Capability values must be JSON +serializable. Pass null to unset the capability.

      +
      Returns
      webdriver.Capabilities

      A self reference.

      +

      setAlertBehavior(behavior)code »

      Sets the default action to take with an unexpected alert before returning +an error.

      +
      Parameters
      behaviorstring

      The desired behavior; should be "accept", "dismiss", +or "ignore". Defaults to "dismiss".

      +
      Returns
      webdriver.Capabilities

      A self reference.

      +

      setEnableNativeEvents(enabled)code »

      Sets whether native events should be used.

      +
      Parameters
      enabledboolean

      Whether to enable native events.

      +
      Returns
      webdriver.Capabilities

      A self reference.

      +

      setLoggingPrefs(prefs)code »

      Sets the logging preferences. Preferences may be specified as a +webdriver.logging.Preferences instance, or a as a map of log-type to +log-level.

      +
      Parameters
      prefs(webdriver.logging.Preferences|Object<string, string>)

      The +logging preferences.

      +
      Returns
      webdriver.Capabilities

      A self reference.

      +

      setProxy(proxy)code »

      Sets the proxy configuration for this instance.

      +
      Parameters
      proxy{proxyType: string}

      The desired proxy configuration.

      +
      Returns
      webdriver.Capabilities

      A self reference.

      +

      setScrollBehavior(behavior)code »

      Sets how elements should be scrolled into view for interaction.

      +
      Parameters
      behaviornumber

      The desired scroll behavior: either 0 to align with +the top of the viewport or 1 to align with the bottom.

      +
      Returns
      webdriver.Capabilities

      A self reference.

      +

      Static Functions

      Capabilities.android()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for Android.

      +

      Capabilities.chrome()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for Chrome.

      +

      Capabilities.firefox()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for Firefox.

      +

      Capabilities.htmlunit()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for HTMLUnit.

      +

      Capabilities.htmlunitwithjs()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for HTMLUnit +with enabled Javascript.

      +

      Capabilities.ie()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for +Internet Explorer.

      +

      Capabilities.ipad()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for iPad.

      +

      Capabilities.iphone()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for iPhone.

      +

      Capabilities.opera()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for Opera.

      +

      Capabilities.phantomjs()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for +PhantomJS.

      +

      Capabilities.safari()code »

      Returns
      webdriver.Capabilities

      A basic set of capabilities for Safari.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_Command.html b/docs/class_webdriver_Command.html index 77ec1da..b6f001a 100644 --- a/docs/class_webdriver_Command.html +++ b/docs/class_webdriver_Command.html @@ -1 +1,15 @@ -webdriver.Command

      Class webdriver.Command

      code »

      Describes a command to be executed by the WebDriverJS framework.

      Constructor

      webdriver.Command ( name )
      Parameters
      name: !webdriver.CommandName
      The name of this command.
      Show:

      Instance Methods

      Returns
      This command's name.
      code »getParameter ( key )*

      Returns a named command parameter.

      Parameters
      key: string
      The parameter key to look up.
      Returns
      The parameter value, or undefined if it has not been set.
      Returns
      The parameters to send with this command.

      Sets a parameter to send with this command.

      Parameters
      name: string
      The parameter name.
      value: *
      The parameter value.
      Returns
      A self reference.

      Sets the parameters for this command.

      Parameters
      parameters: !Object
      The command parameters.
      Returns
      A self reference.

      Instance Properties

      The parameters to this command.

      \ No newline at end of file +Command

      class Command

      Describes a command to be executed by the WebDriverJS framework.

      +

      new Command(name)

      Parameters
      namestring

      The name of this command.

      +

      Instance Methods

      getName()code »

      Returns
      string

      This command's name.

      +

      getParameter(key)code »

      Returns a named command parameter.

      +
      Parameters
      keystring

      The parameter key to look up.

      +
      Returns

      The parameter value, or undefined if it has not been set.

      +

      getParameters()code »

      Returns
      Object<?, *>

      The parameters to send with this command.

      +

      setParameter(name, value)code »

      Sets a parameter to send with this command.

      +
      Parameters
      namestring

      The parameter name.

      +
      value*

      The parameter value.

      +
      Returns
      webdriver.Command

      A self reference.

      +

      setParameters(parameters)code »

      Sets the parameters for this command.

      +
      Parameters
      parametersObject<?, *>

      The command parameters.

      +
      Returns
      webdriver.Command

      A self reference.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_EventEmitter.html b/docs/class_webdriver_EventEmitter.html index 0289852..0a627b2 100644 --- a/docs/class_webdriver_EventEmitter.html +++ b/docs/class_webdriver_EventEmitter.html @@ -1,7 +1,35 @@ -webdriver.EventEmitter

      Class webdriver.EventEmitter

      code »

      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.

      Constructor

      webdriver.EventEmitter ( )
      Show:

      Instance Methods

      code »addListener ( type, listenerFn, opt_scope )!webdriver.EventEmitter

      Registers a listener.

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      Returns
      A self reference.
      code »addListener_ ( type, listenerFn, opt_scope, opt_oneshot )!webdriver.EventEmitter

      Registers a listener.

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      opt_oneshot: boolean=
      Whether the listener should be removed after - the first event is fired.
      Returns
      A self reference.
      code »emit ( type, var_args )

      Fires an event and calls all listeners.

      Parameters
      type: string
      The type of event to emit.
      var_args: ...*
      Any arguments to pass to each listener.
      code »listeners ( type )!Array

      Returns a mutable list of listeners for a specific type of event.

      Parameters
      type: string
      The type of event to retrieve the listeners for.
      Returns
      The registered listeners for - the given event type.
      code »on ( type, listenerFn, opt_scope )!webdriver.EventEmitter

      An alias for #addListener().

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      Returns
      A self reference.
      code »once ( type, listenerFn, opt_scope )!webdriver.EventEmitter

      Registers a one-time listener which will be called only the first time an - event is emitted, after which it will be removed.

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      Returns
      A self reference.

      Removes all listeners for a specific type of event. If no event is - specified, all listeners across all types will be removed.

      Parameters
      opt_type: string=
      The type of event to remove listeners from.
      Returns
      A self reference.

      Removes a previously registered event listener.

      Parameters
      type: string
      The type of event to unregister.
      listenerFn: !Function
      The handler function to remove.
      Returns
      A self reference.

      Instance Properties

      Map of events to registered listeners.

      \ No newline at end of file +EventEmitter

      class EventEmitter

      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.

      +

      new EventEmitter()

      Parameters
      None.

      Instance Methods

      addListener(type, listenerFn, opt_scope)code »

      Registers a listener.

      +
      Parameters
      typestring

      The type of event to listen for.

      +
      listenerFnFunction

      The function to invoke when the event is fired.

      +
      opt_scope?Object=

      The object in whose scope to invoke the listener.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      emit(type, var_args)code »

      Fires an event and calls all listeners.

      +
      Parameters
      typestring

      The type of event to emit.

      +
      var_args...*

      Any arguments to pass to each listener.

      +

      listeners(type)code »

      Returns a mutable list of listeners for a specific type of event.

      +
      Parameters
      typestring

      The type of event to retrieve the listeners for.

      +
      Returns
      Array<{fn: Function, oneshot: boolean, scope: ?(Object)}>

      The registered listeners for +the given event type.

      +

      on(type, listenerFn, opt_scope)code »

      An alias for #addListener().

      +
      Parameters
      typestring

      The type of event to listen for.

      +
      listenerFnFunction

      The function to invoke when the event is fired.

      +
      opt_scope?Object=

      The object in whose scope to invoke the listener.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      once(type, listenerFn, opt_scope)code »

      Registers a one-time listener which will be called only the first time an +event is emitted, after which it will be removed.

      +
      Parameters
      typestring

      The type of event to listen for.

      +
      listenerFnFunction

      The function to invoke when the event is fired.

      +
      opt_scope?Object=

      The object in whose scope to invoke the listener.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      removeAllListeners(opt_type)code »

      Removes all listeners for a specific type of event. If no event is +specified, all listeners across all types will be removed.

      +
      Parameters
      opt_typestring=

      The type of event to remove listeners from.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      removeListener(type, listenerFn)code »

      Removes a previously registered event listener.

      +
      Parameters
      typestring

      The type of event to unregister.

      +
      listenerFnFunction

      The handler function to remove.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_FileDetector.html b/docs/class_webdriver_FileDetector.html new file mode 100644 index 0000000..678ec82 --- /dev/null +++ b/docs/class_webdriver_FileDetector.html @@ -0,0 +1,21 @@ +FileDetector

      class FileDetector

      Used with WebElement#sendKeys on file +input elements (<input type="file">) to detect when the entered key +sequence defines the path to a file.

      +

      By default, WebElement's will enter all +key sequences exactly as entered. You may set a +file detector on the parent +WebDriver instance to define custom behavior for handling file elements. Of +particular note is the selenium-webdriver/remote.FileDetector, which +should be used when running against a remote +Selenium Server.

      +

      new FileDetector()

      Parameters
      None.

      Instance Methods

      handleFile(driver, path)code »

      Handles the file specified by the given path, preparing it for use with +the current browser. If the path does not refer to a valid file, it will +be returned unchanged, otherwisee a path suitable for use with the current +browser will be returned.

      +

      This default implementation is a no-op. Subtypes may override this +function for custom tailored file handling.

      +
      Parameters
      driverwebdriver.WebDriver

      The driver for the current browser.

      +
      pathstring

      The path to process.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise for the processed +file path.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_FirefoxDomExecutor.html b/docs/class_webdriver_FirefoxDomExecutor.html index c36dcb1..6057523 100644 --- a/docs/class_webdriver_FirefoxDomExecutor.html +++ b/docs/class_webdriver_FirefoxDomExecutor.html @@ -1,2 +1,2 @@ -webdriver.FirefoxDomExecutor

      Class webdriver.FirefoxDomExecutor

      code »
      All implemented interfaces:
      webdriver.CommandExecutor

      Constructor

      webdriver.FirefoxDomExecutor ( )

      Enumerations

      webdriver.FirefoxDomExecutor.Attribute_
      Attributes used to communicate with the FirefoxDriver extension.
      webdriver.FirefoxDomExecutor.EventType_
      Events used to communicate with the FirefoxDriver extension.
      Show:

      Instance Methods

      code »execute ( command, callback )
      Parameters
      command
      callback

      Instance Properties

      code »pendingCommand_ : ?{name: string, callback: !Function}

      The pending command, if any.

      Static Functions

      Returns
      Whether the current environment supports the - FirefoxDomExecutor.
      \ No newline at end of file +webdriver.FirefoxDomExecutor

      Class webdriver.FirefoxDomExecutor

      code »
      All implemented interfaces:
      webdriver.CommandExecutor

      Constructor

      webdriver.FirefoxDomExecutor ( )

      Enumerations

      webdriver.FirefoxDomExecutor.Attribute_
      Attributes used to communicate with the FirefoxDriver extension.
      webdriver.FirefoxDomExecutor.EventType_
      Events used to communicate with the FirefoxDriver extension.
      Show:

      Instance Methods

      code »execute ( command, callback )
      Parameters
      command
      callback

      Instance Properties

      code »pendingCommand_ : ?{name: string, callback: !Function}

      The pending command, if any.

      Static Functions

      Returns
      Whether the current environment supports the + FirefoxDomExecutor.
      \ No newline at end of file diff --git a/docs/class_webdriver_Locator.html b/docs/class_webdriver_Locator.html index c6876e3..8960051 100644 --- a/docs/class_webdriver_Locator.html +++ b/docs/class_webdriver_Locator.html @@ -1,2 +1,12 @@ -webdriver.Locator

      Class webdriver.Locator

      code »

      An element locator.

      Constructor

      webdriver.Locator ( using, value )
      Parameters
      using: string
      The type of strategy to use for this locator.
      value: string
      The search target of this locator.
      Show:

      Instance Methods

      code »toString ( )string

      Instance Properties

      The search strategy to use when searching for an element.

      The search target for this locator.

      Static Functions

      Verifies that a value is a valid locator to use for searching for - elements on the page.

      Parameters
      value: *
      The value to check is a valid locator.
      Returns
      A valid locator object or function.
      Throws
      TypeError
      If the given value is an invalid locator.

      Creates a factory function for a webdriver.Locator.

      Parameters
      type: string
      The type of locator for the factory.
      Returns
      The new factory function.
      \ No newline at end of file +Locator

      class Locator

      An element locator.

      +

      new Locator(using, value)

      Parameters
      usingstring

      The type of strategy to use for this locator.

      +
      valuestring

      The search target of this locator.

      +

      Instance Methods

      toString()code »

      Returns
      string

      Instance Properties

      usingstring

      The search strategy to use when searching for an element.

      +
      valuestring

      The search target for this locator.

      +

      Static Functions

      Locator.checkLocator(value)code »

      Verifies that a value is a valid locator to use for searching for +elements on the page.

      +
      Parameters
      value*

      The value to check is a valid locator.

      +
      Returns
      (webdriver.Locator|Function)

      A valid locator object or function.

      +
      Throws
      TypeError

      If the given value is an invalid locator.

      +

      Static Properties

      Locator.StrategyObject<string, function(string): (Function|webdriver.Locator)>

      Maps webdriver.By.Hash keys to the appropriate factory function.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_Serializable.html b/docs/class_webdriver_Serializable.html new file mode 100644 index 0000000..df789cf --- /dev/null +++ b/docs/class_webdriver_Serializable.html @@ -0,0 +1,9 @@ +Serializable

      class Serializable<T>

      Defines an object that can be asynchronously serialized to its WebDriver +wire representation.

      +

      new Serializable()

      Parameters
      None.

      Instance Methods

      serialize()code »

      Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

      +
      Returns
      (T|IThenable<T>)

      This instance's serialized wire format.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_Session.html b/docs/class_webdriver_Session.html index 5aa83a4..689fa4b 100644 --- a/docs/class_webdriver_Session.html +++ b/docs/class_webdriver_Session.html @@ -1,3 +1,13 @@ -webdriver.Session

      Class webdriver.Session

      code »

      Contains information about a WebDriver session.

      Constructor

      webdriver.Session ( id, capabilities )
      Parameters
      id: string
      The session ID.
      capabilities: !(Object|webdriver.Capabilities)
      The session - capabilities.
      Show:

      Instance Methods

      Returns
      This session's capabilities.
      code »getCapability ( key )*

      Retrieves the value of a specific capability.

      Parameters
      key: string
      The capability to retrieve.
      Returns
      The capability value.
      Returns
      This session's ID.

      Returns the JSON representation of this object, which is just the string - session ID.

      Returns
      The JSON representation of this Session.

      Instance Properties

      \ No newline at end of file +Session

      class Session

      Contains information about a WebDriver session.

      +

      new Session(id, capabilities)

      Parameters
      idstring

      The session ID.

      +
      capabilitiesObject

      The session +capabilities.

      +

      Instance Methods

      getCapabilities()code »

      Returns
      webdriver.Capabilities

      This session's capabilities.

      +

      getCapability(key)code »

      Retrieves the value of a specific capability.

      +
      Parameters
      keystring

      The capability to retrieve.

      +
      Returns

      The capability value.

      +

      getId()code »

      Returns
      string

      This session's ID.

      +

      toJSON(arg0)code »

      Returns the JSON representation of this object, which is just the string +session ID.

      +
      Parameters
      arg0string=
      Returns
      string

      The JSON representation of this Session.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_TouchSequence.html b/docs/class_webdriver_TouchSequence.html new file mode 100644 index 0000000..5ea0593 --- /dev/null +++ b/docs/class_webdriver_TouchSequence.html @@ -0,0 +1,49 @@ +TouchSequence

      class TouchSequence

      Class for defining sequences of user touch interactions. Each sequence +will not be executed until #perform is called.

      +

      Example:

      +
      new webdriver.TouchSequence(driver).
      +    tapAndHold({x: 0, y: 0}).
      +    move({x: 3, y: 4}).
      +    release({x: 10, y: 10}).
      +    perform();
      +
      +

      new TouchSequence(driver)

      Parameters
      driverwebdriver.WebDriver

      The driver instance to use.

      +

      Instance Methods

      doubleTap(elem)code »

      Double taps an element.

      +
      Parameters
      elemwebdriver.WebElement

      The element to double tap.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      flick(speed)code »

      Flick, starting anywhere on the screen, at speed xspeed and yspeed.

      +
      Parameters
      speed{xspeed: number, yspeed: number}

      The speed to flick in each +direction, in pixels per second.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      flickElement(elem, offset, speed)code »

      Flick starting at elem and moving by x and y at specified speed.

      +
      Parameters
      elemwebdriver.WebElement

      The element where flick starts.

      +
      offset{x: number, y: number}

      The offset to flick to.

      +
      speednumber

      The speed to flick at in pixels per second.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      longPress(elem)code »

      Long press on an element.

      +
      Parameters
      elemwebdriver.WebElement

      The element to long press.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      move(location)code »

      Move a held touch to the specified location.

      +
      Parameters
      location{x: number, y: number}

      The location to move to.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      perform()code »

      Executes this action sequence.

      +
      Returns
      webdriver.promise.Promise

      A promise that will be resolved once +this sequence has completed.

      +

      release(location)code »

      Release a held touch at the specified location.

      +
      Parameters
      location{x: number, y: number}

      The location to release at.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      scroll(offset)code »

      Scrolls the touch screen by the given offset.

      +
      Parameters
      offset{x: number, y: number}

      The offset to scroll to.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      scrollFromElement(elem, offset)code »

      Scrolls the touch screen, starting on elem and moving by the specified +offset.

      +
      Parameters
      elemwebdriver.WebElement

      The element where scroll starts.

      +
      offset{x: number, y: number}

      The offset to scroll to.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      tap(elem)code »

      Taps an element.

      +
      Parameters
      elemwebdriver.WebElement

      The element to tap.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +

      tapAndHold(location)code »

      Touch down at the given location.

      +
      Parameters
      location{x: number, y: number}

      The location to touch down at.

      +
      Returns
      webdriver.TouchSequence

      A self reference.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_UnhandledAlertError.html b/docs/class_webdriver_UnhandledAlertError.html index c8c53b4..e2ea0ca 100644 --- a/docs/class_webdriver_UnhandledAlertError.html +++ b/docs/class_webdriver_UnhandledAlertError.html @@ -1,6 +1,18 @@ -webdriver.UnhandledAlertError

      Class webdriver.UnhandledAlertError

      code »
      Error
      +UnhandledAlertError

      An error returned to indicate that there is an unhandled modal dialog on the - current page.

      Constructor

      webdriver.UnhandledAlertError ( message, alert )
      Parameters
      message: string
      The error message.
      alert: !webdriver.Alert
      The alert handle.
      Show:

      Instance Methods

      Defined in webdriver.UnhandledAlertError

      Returns
      The open alert.

      Defined in bot.Error

      Returns
      he string representation of this error.

      Instance Properties

      Defined in webdriver.UnhandledAlertError

      Defined in bot.Error

      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.

      Static Properties

      \ No newline at end of file + └ webdriver.UnhandledAlertError

      An error returned to indicate that there is an unhandled modal dialog on the +current page.

      +

      new UnhandledAlertError(message, text, alert)

      Parameters
      messagestring

      The error message.

      +
      textstring

      The text displayed with the unhandled alert.

      +
      alertwebdriver.Alert

      The alert handle.

      +

      Instance Methods

      getAlertText()code »

      Returns
      string

      The text displayed with the unhandled alert.

      +

      toString()code »

      Defined by: bot.Error

      Returns
      string

      The string representation of this error.

      +

      Instance Properties

      codenumber

      This error's status code.

      +
      descriptionstring

      IE-only.

      +
      fileNamestring

      Mozilla-only

      +
      isAutomationErrorboolean

      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.

      +
      lineNumbernumber

      Mozilla-only.

      +
      messagestring
      No description.
      namestring
      No description.
      sourceURL?

      Doesn't seem to exist, but closure/debug.js references it.

      +
      stackstring
      No description.
      statestring
      No description.
      \ No newline at end of file diff --git a/docs/class_webdriver_WebDriver.html b/docs/class_webdriver_WebDriver.html index 4607477..9d778c4 100644 --- a/docs/class_webdriver_WebDriver.html +++ b/docs/class_webdriver_WebDriver.html @@ -1,251 +1,313 @@ -webdriver.WebDriver

      Class webdriver.WebDriver

      code »

      Creates a new WebDriver client, which provides control over a browser. +WebDriver

      class WebDriver

      Creates a new WebDriver client, which provides control over a browser.

      +

      Every WebDriver command returns a 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'));
      +});
      +
      +

      new WebDriver(session, executor, opt_flow)

      Parameters
      session(webdriver.Session|webdriver.promise.Promise)

      Either a +known session or a promise that will be resolved to a session.

      +
      executorwebdriver.CommandExecutor

      The executor to use when +sending commands to the browser.

      +
      opt_flow?webdriver.promise.ControlFlow=

      The flow to +schedule commands through. Defaults to the active flow object.

      +

      Instance Methods

      actions()code »

      Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

      +
      driver.actions().
      +    mouseDown(element1).
      +    mouseMove(element2).
      +    mouseUp().
      +    perform();
      +
      +
      Returns
      webdriver.ActionSequence

      A new action sequence for this instance.

      +

      <T> call(fn, opt_scope, var_args)code »

      Schedules a command to execute a custom function.

      +
      Parameters
      fnfunction(...?): (T|webdriver.promise.Promise<T>)

      The function to +execute.

      +
      opt_scope?Object=

      The object in whose scope to execute the function.

      +
      var_args...*

      Any arguments to pass to the function.

      +
      Returns
      webdriver.promise.Promise<T>

      A promise that will be resolved' +with the function's result.

      +

      close()code »

      Schedules a command to close the current window.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when this command has completed.

      +

      controlFlow()code »

      Returns
      webdriver.promise.ControlFlow

      The control flow used by this +instance.

      +

      <T> executeAsyncScript(script, var_args)code »

      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 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 #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:

      +
      • For a HTML element, the value will resolve to a +webdriver.WebElement
      • Null and undefined return values will resolve to null
      • Booleans, numbers, and strings will resolve as is
      • Functions will resolve to their string representation
      • For arrays and objects, each member item will be converted according to +the rules above
      +

      Example #1: Performing a sleep that is synchronized with the currently +selected window:

      +
      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 #2: Synchronizing a test with an AJAX application:

      +
      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');
      +
      +

      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.

      +
      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.responseText);
      +    }
      +  }
      +  xhr.send('');
      +}).then(function(str) {
      +  console.log(JSON.parse(str)['food']);
      +});
      +
      +
      Parameters
      script(string|Function)

      The script to execute.

      +
      var_args...*

      The arguments to pass to the script.

      +
      Returns
      webdriver.promise.Promise<T>

      A promise that will resolve to the +scripts return value.

      +

      <T> executeScript(script, var_args)code »

      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 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:

      +
      • For a HTML element, the value will resolve to a +webdriver.WebElement
      • Null and undefined return values will resolve to null
      • Booleans, numbers, and strings will resolve as is
      • Functions will resolve to their string representation
      • For arrays and objects, each member item will be converted according to +the rules above
      +
      Parameters
      script(string|Function)

      The script to execute.

      +
      var_args...*

      The arguments to pass to the script.

      +
      Returns
      webdriver.promise.Promise<T>

      A promise that will resolve to the +scripts return value.

      +

      findElement(locator)code »

      Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

      +

      The search criteria for an element may be defined using one of the +factories in the 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'});
      +
      +

      You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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);
       
      - Every WebDriver command returns a 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

      webdriver.WebDriver ( session, executor, opt_flow )
      Parameters
      session: !(webdriver.Session|webdriver.promise.Promise)
      Either a - known session or a promise that will be resolved to a session.
      executor: !webdriver.CommandExecutor
      The executor to use when - sending commands to the browser.
      opt_flow: webdriver.promise.ControlFlow=
      The flow to - schedule commands through. Defaults to the active flow object.

      Classes

      webdriver.WebDriver.Logs
      Interface for managing WebDriver log records.
      webdriver.WebDriver.Navigation
      Interface for navigating back and forth in the browser history.
      webdriver.WebDriver.Options
      Provides methods for managing browser and driver state.
      webdriver.WebDriver.TargetLocator
      An interface for changing the focus of the driver to another frame or window.
      webdriver.WebDriver.Timeouts
      An interface for managing timeout behavior for WebDriver instances.
      webdriver.WebDriver.Window
      An interface for managing the current window.
      Show:

      Instance Methods

      Creates a new action sequence using this driver. The sequence will not be - scheduled for execution until webdriver.ActionSequence#perform is - called. Example: -

      
      -   driver.actions().
      -       mouseDown(element1).
      -       mouseMove(element2).
      -       mouseUp().
      -       perform();
      - 
      Returns
      A new action sequence for this instance.
      code »call ( fn, opt_scope, var_args )!webdriver.promise.Promise

      Schedules a command to execute a custom function.

      Parameters
      fn: !Function
      The function to execute.
      opt_scope: Object=
      The object in whose scope to execute the function.
      var_args: ...*
      Any arguments to pass to the function.
      Returns
      A promise that will be resolved with the - function's result.

      Schedules a command to close the current window.

      Returns
      A promise that will be resolved when - this command has completed.
      Returns
      The control flow used by this - instance.

      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 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: -

        -
      • For a HTML element, the value will resolve to a - webdriver.WebElement
      • -
      • Null and undefined return values will resolve to null
      • -
      • Booleans, numbers, and strings will resolve as is
      • -
      • Functions will resolve to their string representation
      • -
      • For arrays and objects, each member item will be converted according to - the rules above
      • -
      - - Example #1: Performing a sleep that is synchronized with the currently - selected window: -
      - 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 #2: Synchronizing a test with an AJAX application: -
      - 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');
      - 
      - - 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. -
      - 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']);
      - });
      - 
      Parameters
      script: !(string|Function)
      The script to execute.
      var_args: ...*
      The arguments to pass to the script.
      Returns
      A promise that will resolve to the - scripts return value.

      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 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: -

        -
      • For a HTML element, the value will resolve to a - webdriver.WebElement
      • -
      • Null and undefined return values will resolve to null
      • -
      • Booleans, numbers, and strings will resolve as is
      • -
      • Functions will resolve to their string representation
      • -
      • For arrays and objects, each member item will be converted according to - the rules above
      • -
      Parameters
      script: !(string|Function)
      The script to execute.
      var_args: ...*
      The arguments to pass to the script.
      Returns
      A promise that will resolve to the - scripts return value.

      Locates a DOM element so that commands may be issued against it using the - 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
      element: !Element
      The element to locate.
      Returns
      A promise that will be resolved - with the located WebElement.

      Schedule a command to find an element on the page. If the element cannot be - found, a bot.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. - -

      The search criteria for an element may be defined using one of the - factories in the 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'});
      - 
      - -

      You may also provide a custom locator function, which takes as input - this WebDriver instance and returns a 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];
      -   });
      - }
      - 
      - -

      When running in the browser, a WebDriver cannot manipulate DOM elements - directly; it may do so only through a 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
      locator: !(webdriver.Locator|webdriver.By.Hash|Element|Function)
      The - locator to use.
      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.
      Parameters
      locatorFn: !Function
      The locator function to use.
      context: !(webdriver.WebDriver|webdriver.WebElement)
      The search - context.
      Returns
      A - promise that will resolve to a list of WebElements.

      Schedule a command to search for multiple elements on the page.

      Parameters
      locator: !(webdriver.Locator|webdriver.By.Hash|Function)
      The locator - strategy to use when searching for the element.
      Returns
      A - promise that will resolve to an array of WebElements.
      Parameters
      locatorFn: !Function
      The locator function to use.
      context: !(webdriver.WebDriver|webdriver.WebElement)
      The search - context.
      Returns
      A - promise that will resolve to an array of WebElements.

      Schedules a command to navigate to the given URL.

      Parameters
      url: string
      The fully qualified URL to open.
      Returns
      A promise that will be resolved when the - document has finished loading.

      Schedules a command to retrieve the current list of available window handles.

      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.

      Schedules a command to retrieve the URL of the current page.

      Returns
      A promise that will be resolved with the - current URL.

      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.

      Returns
      A promise that will be resolved with the - current page source.
      Returns
      A promise for this client's session.

      Schedules a command to retrieve the current page's title.

      Returns
      A promise that will be resolved with the - current page's title.

      Schedules a command to retrieve they current window handle.

      Returns
      A promise that will be resolved with the - 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.

      Parameters
      locatorOrElement: !(webdriver.Locator|webdriver.By.Hash|Element|Function)
      The locator to use, or the actual - DOM element to be located by the server.
      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.

      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.

      Returns
      A promise that will be resolved when - the command has completed.
      code »schedule ( command, description )!webdriver.promise.Promise

      Schedules a webdriver.Command to be executed by this driver's - webdriver.CommandExecutor.

      Parameters
      command: !webdriver.Command
      The command to schedule.
      description: string
      A description of the command for debugging.
      Returns
      A promise that will be resolved with - the command result.

      Schedules a command to make the driver sleep for the given amount of time.

      Parameters
      ms: number
      The amount of time, in milliseconds, to sleep.
      Returns
      A promise that will be resolved when the - sleep has finished.
      Returns
      The target locator interface for - this instance.

      Schedule a command to take a screenshot. The driver makes a best effort to - return a screenshot of the following, in order of preference: -

        -
      1. Entire page -
      2. Current window -
      3. Visible portion of the current frame -
      4. The screenshot of the entire display containing the browser -
      Returns
      A promise that will be resolved to the - screenshot as a base-64 encoded PNG.
      code »wait ( fn, timeout, opt_message )!webdriver.promise.Promise

      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.

      Parameters
      fn: function(): boolean
      The function to evaluate as a wait condition.
      timeout: number
      How long to wait for the condition to be true.
      opt_message: string=
      An optional message to use if the wait times - out.
      Returns
      A promise that will be resolved when the - wait condition has been satisfied.

      Instance Properties

      Static Functions

      code »webdriver.WebDriver.acquireSession_ ( executor, command, description )!webdriver.WebDriver

      Sends a command to the server that is expected to return the details for a - webdriver.Session. This may either be an existing session, or a - newly created one.

      Parameters
      executor: !webdriver.CommandExecutor
      Command executor to use when - querying for session details.
      command: !webdriver.Command
      The command to send to fetch the session - details.
      description: string
      A descriptive debug label for this action.
      Returns
      A new WebDriver client for the session.

      Creates a new WebDriver client for an existing session.

      Parameters
      executor: !webdriver.CommandExecutor
      Command executor to use when - querying for session details.
      sessionId: string
      ID of the session to attach to.
      Returns
      A new client for the specified session.
      code »webdriver.WebDriver.createSession ( executor, desiredCapabilities )!webdriver.WebDriver

      Creates a new WebDriver session.

      Parameters
      executor: !webdriver.CommandExecutor
      The executor to create the new - session with.
      desiredCapabilities: !webdriver.Capabilities
      The desired - capabilities for the new session.
      Returns
      The driver for the newly created session.

      Translates a command to its wire-protocol representation before passing it - to the given executor for execution.

      Parameters
      executor: !webdriver.CommandExecutor
      The executor to use.
      command: !webdriver.Command
      The command to execute.
      Returns
      A promise that will resolve with the - command response.

      Converts a value from its JSON representation according to the WebDriver wire - protocol. Any JSON object containing a - webdriver.WebElement.ELEMENT_KEY key will be decoded to a - webdriver.WebElement object. All other values will be passed through - as is.

      Parameters
      driver: !webdriver.WebDriver
      The driver instance to use as the - parent of any unwrapped webdriver.WebElement values.
      value: *
      The value to convert.
      Returns
      The converted value.

      Converts an object to its JSON representation in the WebDriver wire protocol. - When converting values of type object, the following steps will be taken: -

        -
      1. if the object provides a "toWireValue" function, the return value will - be returned in its fully resolved state (e.g. this function may return - promise values)
      2. -
      3. if the object provides a "toJSON" function, the return value of this - function will be returned
      4. -
      5. otherwise, the value of each key will be recursively converted according - to the rules above.
      6. -
      Parameters
      obj: *
      The object to convert.
      Returns
      A promise that will resolve to the - input value's JSON representation.
      \ No newline at end of file +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]; + }); +} + +

      When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The +locator to use.

      +
      Returns
      webdriver.WebElement

      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.

      +

      findElements(locator)code »

      Schedule a command to search for multiple elements on the page.

      +
      Parameters
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The locator +strategy to use when searching for the element.

      +
      Returns
      webdriver.promise.Promise<Array<webdriver.WebElement>>

      A +promise that will resolve to an array of WebElements.

      +

      get(url)code »

      Schedules a command to navigate to the given URL.

      +
      Parameters
      urlstring

      The fully qualified URL to open.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the document has finished loading.

      +

      getAllWindowHandles()code »

      Schedules a command to retrieve the current list of available window handles.

      +
      Returns
      webdriver.promise.Promise<Array<string>>

      A promise that will +be resolved with an array of window handles.

      +

      getCapabilities()code »

      Returns
      webdriver.promise.Promise<webdriver.Capabilities>

      A promise +that will resolve with the this instance's capabilities.

      +

      getCurrentUrl()code »

      Schedules a command to retrieve the URL of the current page.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the current URL.

      +

      getPageSource()code »

      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.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the current page source.

      +

      getSession()code »

      Returns
      webdriver.promise.Promise<webdriver.Session>

      A promise for this +client's session.

      +

      getTitle()code »

      Schedules a command to retrieve the current page's title.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the current page's title.

      +

      getWindowHandle()code »

      Schedules a command to retrieve they current window handle.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the current window handle.

      +

      isElementPresent(locatorOrElement)code »

      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.

      +
      Parameters
      locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The locator to use, or the actual +DOM element to be located by the server.

      +
      Returns
      webdriver.promise.Promise<boolean>

      A promise that will resolve +with whether the element is present on the page.

      +

      manage()code »

      Returns
      webdriver.WebDriver.Options

      The options interface for this +instance.

      +


      quit()code »

      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.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the command has completed.

      +

      <T> schedule(command, description)code »

      Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

      +
      Parameters
      commandwebdriver.Command

      The command to schedule.

      +
      descriptionstring

      A description of the command for debugging.

      +
      Returns
      webdriver.promise.Promise<T>

      A promise that will be resolved +with the command result.

      +

      setFileDetector(detector)code »

      Sets the file detector that should be +used with this instance.

      +
      Parameters
      detectorwebdriver.FileDetector

      The detector to use or null.

      +

      sleep(ms)code »

      Schedules a command to make the driver sleep for the given amount of time.

      +
      Parameters
      msnumber

      The amount of time, in milliseconds, to sleep.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the sleep has finished.

      +

      switchTo()code »

      Returns
      webdriver.WebDriver.TargetLocator

      The target locator interface for +this instance.

      +

      takeScreenshot()code »

      Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

      +
      1. Entire page +
      2. Current window +
      3. Visible portion of the current frame +
      4. The screenshot of the entire display containing the browser +
      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

      +

      touchActions()code »

      Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

      +
      driver.touchActions().
      +    tap(element1).
      +    doubleTap(element2).
      +    perform();
      +
      +
      Returns
      webdriver.TouchSequence

      A new touch sequence for this instance.

      +

      <T> wait(condition, opt_timeout, opt_message)code »

      Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

      +

      For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

      +

      Example: waiting up to 10 seconds for an element to be present and visible +on the page.

      +
      var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
      +button.click();
      +
      +

      This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

      +

      Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

      +
      var started = startTestServer();
      +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
      +driver.get(getServerUrl());
      +
      +
      Parameters
      condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

      The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

      +
      opt_timeoutnumber=

      How long to wait for the condition to be true.

      +
      opt_messagestring=

      An optional message to use if the wait times +out.

      +
      Returns
      webdriver.promise.Promise<T>

      A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

      +

      Static Functions

      WebDriver.attachToSession(executor, sessionId, opt_flow)code »

      Creates a new WebDriver client for an existing session.

      +
      Parameters
      executorwebdriver.CommandExecutor

      Command executor to use when +querying for session details.

      +
      sessionIdstring

      ID of the session to attach to.

      +
      opt_flow?webdriver.promise.ControlFlow=

      The control flow all driver +commands should execute under. Defaults to the +currently active control flow.

      +
      Returns
      webdriver.WebDriver

      A new client for the specified session.

      +

      WebDriver.createSession(executor, desiredCapabilities, opt_flow)code »

      Creates a new WebDriver session.

      +
      Parameters
      executorwebdriver.CommandExecutor

      The executor to create the new +session with.

      +
      desiredCapabilitieswebdriver.Capabilities

      The desired +capabilities for the new session.

      +
      opt_flow?webdriver.promise.ControlFlow=

      The control flow all driver +commands should execute under, including the initial session creation. +Defaults to the currently active +control flow.

      +
      Returns
      webdriver.WebDriver

      The driver for the newly created session.

      +

      Types

      WebDriver.Logs

      Interface for managing WebDriver log records.

      +
      WebDriver.Navigation

      Interface for navigating back and forth in the browser history.

      +
      WebDriver.Options

      Provides methods for managing browser and driver state.

      +
      WebDriver.TargetLocator

      An interface for changing the focus of the driver to another frame or window.

      +
      WebDriver.Timeouts

      An interface for managing timeout behavior for WebDriver instances.

      +
      WebDriver.Window

      An interface for managing the current window.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebDriver_Logs.html b/docs/class_webdriver_WebDriver_Logs.html index 7530f25..aeb76be 100644 --- a/docs/class_webdriver_WebDriver_Logs.html +++ b/docs/class_webdriver_WebDriver_Logs.html @@ -1,10 +1,15 @@ -webdriver.WebDriver.Logs

      Class webdriver.WebDriver.Logs

      code »

      Interface for managing WebDriver log records.

      Constructor

      webdriver.WebDriver.Logs ( driver )
      Parameters
      driver: !webdriver.WebDriver
      The parent driver.
      Show:

      Instance Methods

      Fetches available log entries for the given type. - -

      Note that log buffers are reset after each call, meaning that - available log entries correspond to those entries not yet returned for a - given log type. In practice, this means that this call will return the - available log entries since the last call, or from the start of the - session.

      Parameters
      type: !webdriver.logging.Type
      The desired log type.
      Returns
      A - promise that will resolve to a list of log entries for the specified - type.

      Retrieves the log types available to this driver.

      Returns
      A - promise that will resolve to a list of available log types.

      Instance Properties

      \ No newline at end of file +WebDriver.Logs

      class WebDriver.Logs

      Interface for managing WebDriver log records.

      +

      new WebDriver.Logs(driver)

      Parameters
      driverwebdriver.WebDriver

      The parent driver.

      +

      Instance Methods

      get(type)code »

      Fetches available log entries for the given type.

      +

      Note that log buffers are reset after each call, meaning that available +log entries correspond to those entries not yet returned for a given log +type. In practice, this means that this call will return the available log +entries since the last call, or from the start of the session.

      +
      Parameters
      typewebdriver.logging.Type

      The desired log type.

      +
      Returns
      webdriver.promise.Promise<Array<webdriver.logging.Entry>>

      A +promise that will resolve to a list of log entries for the specified +type.

      +

      getAvailableLogTypes()code »

      Retrieves the log types available to this driver.

      +
      Returns
      webdriver.promise.Promise<Array<webdriver.logging.Type>>

      A +promise that will resolve to a list of available log types.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebDriver_Navigation.html b/docs/class_webdriver_WebDriver_Navigation.html index efe2114..16af66c 100644 --- a/docs/class_webdriver_WebDriver_Navigation.html +++ b/docs/class_webdriver_WebDriver_Navigation.html @@ -1,5 +1,16 @@ -webdriver.WebDriver.Navigation

      Class webdriver.WebDriver.Navigation

      code »

      Interface for navigating back and forth in the browser history.

      Constructor

      webdriver.WebDriver.Navigation ( driver )
      Parameters
      driver: !webdriver.WebDriver
      The parent driver.
      Show:

      Instance Methods

      Schedules a command to move backwards in the browser history.

      Returns
      A promise that will be resolved when the - navigation event has completed.

      Schedules a command to move forwards in the browser history.

      Returns
      A promise that will be resolved when the - navigation event has completed.

      Schedules a command to refresh the current page.

      Returns
      A promise that will be resolved when the - navigation event has completed.

      Schedules a command to navigate to a new URL.

      Parameters
      url: string
      The URL to navigate to.
      Returns
      A promise that will be resolved when the - URL has been loaded.

      Instance Properties

      \ No newline at end of file +WebDriver.Navigation

      class WebDriver.Navigation

      Interface for navigating back and forth in the browser history.

      +

      new WebDriver.Navigation(driver)

      Parameters
      driverwebdriver.WebDriver

      The parent driver.

      +

      Instance Methods

      back()code »

      Schedules a command to move backwards in the browser history.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the navigation event has completed.

      +

      forward()code »

      Schedules a command to move forwards in the browser history.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the navigation event has completed.

      +

      refresh()code »

      Schedules a command to refresh the current page.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the navigation event has completed.

      +

      to(url)code »

      Schedules a command to navigate to a new URL.

      +
      Parameters
      urlstring

      The URL to navigate to.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the URL has been loaded.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebDriver_Options.html b/docs/class_webdriver_WebDriver_Options.html index ee252eb..6e0cc55 100644 --- a/docs/class_webdriver_WebDriver_Options.html +++ b/docs/class_webdriver_WebDriver_Options.html @@ -1,16 +1,41 @@ -webdriver.WebDriver.Options

      Class webdriver.WebDriver.Options

      code »

      Provides methods for managing browser and driver state.

      Constructor

      webdriver.WebDriver.Options ( driver )
      Parameters
      driver: !webdriver.WebDriver
      The parent driver.
      Show:

      Instance Methods

      code »addCookie ( name, value, opt_path, opt_domain, opt_isSecure, opt_expiry )!webdriver.promise.Promise

      Schedules a command to add a cookie.

      Parameters
      name: string
      The cookie name.
      value: string
      The cookie value.
      opt_path: string=
      The cookie path.
      opt_domain: string=
      The cookie domain.
      opt_isSecure: boolean=
      Whether the cookie is secure.
      opt_expiry: (number|!Date)=
      When the cookie expires. If specified as - a number, should be in milliseconds since midnight, January 1, 1970 UTC.
      Returns
      A promise that will be resolved when the - cookie has been added to the page.

      Schedules a command to delete all cookies visible to the current page.

      Returns
      A promise that will be resolved when all - cookies have been deleted.

      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.

      Parameters
      name: string
      The name of the cookie to delete.
      Returns
      A promise that will be resolved when the - cookie has been deleted.

      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.

      Parameters
      name: string
      The name of the cookie to retrieve.
      Returns
      A promise that will be resolved with the - named cookie, or null if there is no such cookie.

      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.

      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

      \ No newline at end of file +WebDriver.Options

      class WebDriver.Options

      Provides methods for managing browser and driver state.

      +

      new WebDriver.Options(driver)

      Parameters
      driverwebdriver.WebDriver

      The parent driver.

      +

      Instance Methods

      addCookie(name, value, opt_path, opt_domain, opt_isSecure, opt_expiry)code »

      Schedules a command to add a cookie.

      +
      Parameters
      namestring

      The cookie name.

      +
      valuestring

      The cookie value.

      +
      opt_pathstring=

      The cookie path.

      +
      opt_domainstring=

      The cookie domain.

      +
      opt_isSecureboolean=

      Whether the cookie is secure.

      +
      opt_expiry(number|Date)=

      When the cookie expires. If specified as +a number, should be in milliseconds since midnight, January 1, 1970 UTC.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the cookie has been added to the page.

      +

      deleteAllCookies()code »

      Schedules a command to delete all cookies visible to the current page.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when all cookies have been deleted.

      +

      deleteCookie(name)code »

      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.

      +
      Parameters
      namestring

      The name of the cookie to delete.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the cookie has been deleted.

      +

      getCookie(name)code »

      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.

      +
      Parameters
      namestring

      The name of the cookie to retrieve.

      +
      Returns
      webdriver.promise.Promise<?{domain: (string|undefined), expiry: (number|undefined), name: string, path: (string|undefined), secure: (boolean|undefined), value: string}>

      A +promise that will be resolved with the named cookie, or null +if there is no such cookie.

      +

      getCookies()code »

      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.

      +
      Returns
      webdriver.promise.Promise<Array<{domain: (string|undefined), expiry: (number|undefined), name: string, path: (string|undefined), secure: (boolean|undefined), value: string}>>

      A promise that will be +resolved with the cookies visible to the current page.

      +

      logs()code »

      Returns
      webdriver.WebDriver.Logs

      The interface for managing driver +logs.

      +

      timeouts()code »

      Returns
      webdriver.WebDriver.Timeouts

      The interface for managing driver +timeouts.

      +

      window()code »

      Returns
      webdriver.WebDriver.Window

      The interface for managing the +current window.

      +

      Type Definitions

      Options.Cookie{domain: (string|undefined), expiry: (number|undefined), name: string, path: (string|undefined), secure: (boolean|undefined), value: string}

      A JSON description of a browser cookie.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebDriver_TargetLocator.html b/docs/class_webdriver_WebDriver_TargetLocator.html index 8ff60ef..fe6d1b0 100644 --- a/docs/class_webdriver_WebDriver_TargetLocator.html +++ b/docs/class_webdriver_WebDriver_TargetLocator.html @@ -1,27 +1,38 @@ -webdriver.WebDriver.TargetLocator

      Class webdriver.WebDriver.TargetLocator

      code »

      An interface for changing the focus of the driver to another frame or window.

      Constructor

      webdriver.WebDriver.TargetLocator ( driver )
      Parameters
      driver: !webdriver.WebDriver
      The parent driver.
      Show:

      Instance Methods

      Schedules a command retrieve the document.activeElement element on - the current document, or document.body if activeElement is not - available.

      Returns
      The active element.

      Schedules a command to change focus to the active alert dialog. This command - will return a bot.ErrorCode.NO_MODAL_DIALOG_OPEN error if a modal - dialog is not currently open.

      Returns
      The open alert.

      Schedules a command to switch focus of all future commands to the first frame - on the page.

      Returns
      A promise that will be resolved when the - driver has changed focus to the default content.

      Schedules a command to switch the focus of all future commands to another - frame on the page. -

      - If the frame is specified by a number, the command will switch to the frame - by its (zero-based) index into the 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.

      Parameters
      nameOrIndex: (string|number)
      The frame locator.
      Returns
      A promise that will be resolved when the - driver has changed focus to the specified frame.

      Schedules a command to switch the focus of all future commands to another - window. Windows may be specified by their window.name attribute or - by its handle (as returned by webdriver.WebDriver#getWindowHandles). -

      - If the specificed window can not be found, the deferred result will errback - with a bot.ErrorCode.NO_SUCH_WINDOW error.

      Parameters
      nameOrHandle: string
      The name or window handle of the window to - switch focus to.
      Returns
      A promise that will be resolved when the - driver has changed focus to the specified window.

      Instance Properties

      \ No newline at end of file +WebDriver.TargetLocator

      class WebDriver.TargetLocator

      An interface for changing the focus of the driver to another frame or window.

      +

      new WebDriver.TargetLocator(driver)

      Parameters
      driverwebdriver.WebDriver

      The parent driver.

      +

      Instance Methods

      activeElement()code »

      Schedules a command retrieve the document.activeElement element on +the current document, or document.body if activeElement is not +available.

      +
      Returns
      webdriver.WebElementPromise

      The active element.

      +

      alert()code »

      Schedules a command to change focus to the active alert dialog. This command +will return a bot.ErrorCode.NO_SUCH_ALERT error if an alert dialog +is not currently open.

      +
      Returns
      webdriver.AlertPromise

      The open alert.

      +

      defaultContent()code »

      Schedules a command to switch focus of all future commands to the first frame +on the page.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the driver has changed focus to the default content.

      +

      frame(nameOrIndex)code »

      Schedules a command to switch the focus of all future commands to another +frame on the page.

      +

      If the frame is specified by a number, the command will switch to the frame +by its (zero-based) index into +window.frames.

      +

      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.

      +
      Parameters
      nameOrIndex(string|number)

      The frame locator.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the driver has changed focus to the specified frame.

      +

      window(nameOrHandle)code »

      Schedules a command to switch the focus of all future commands to another +window. Windows may be specified by their window.name attribute or +by its handle (as returned by webdriver.WebDriver#getWindowHandles).

      +

      If the specificed window can not be found, the deferred result will errback +with a bot.ErrorCode.NO_SUCH_WINDOW error.

      +
      Parameters
      nameOrHandlestring

      The name or window handle of the window to +switch focus to.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the driver has changed focus to the specified window.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebDriver_Timeouts.html b/docs/class_webdriver_WebDriver_Timeouts.html index 39080e1..85926ca 100644 --- a/docs/class_webdriver_WebDriver_Timeouts.html +++ b/docs/class_webdriver_WebDriver_Timeouts.html @@ -1,21 +1,29 @@ -webdriver.WebDriver.Timeouts

      Class webdriver.WebDriver.Timeouts

      code »

      An interface for managing timeout behavior for WebDriver instances.

      Constructor

      webdriver.WebDriver.Timeouts ( driver )
      Parameters
      driver: !webdriver.WebDriver
      The parent driver.
      Show:

      Instance Methods

      Specifies the amount of time the driver should wait when searching for an - element if it is not immediately present. -

      - When searching for a single element, the driver should poll the page - until the element has been found, or this timeout expires before failing - with a 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
      ms: number
      The amount of time to wait, in milliseconds.
      Returns
      A promise that will be resolved when the - implicit wait timeout has been set.

      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.

      Parameters
      ms: number
      The amount of time to wait, in milliseconds.
      Returns
      A promise that will be resolved when - the timeout has been set.

      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.

      Parameters
      ms: number
      The amount of time to wait, in milliseconds.
      Returns
      A promise that will be resolved when the - script timeout has been set.

      Instance Properties

      \ No newline at end of file +WebDriver.Timeouts

      class WebDriver.Timeouts

      An interface for managing timeout behavior for WebDriver instances.

      +

      new WebDriver.Timeouts(driver)

      Parameters
      driverwebdriver.WebDriver

      The parent driver.

      +

      Instance Methods

      implicitlyWait(ms)code »

      Specifies the amount of time the driver should wait when searching for an +element if it is not immediately present.

      +

      When searching for a single element, the driver should poll the page +until the element has been found, or this timeout expires before failing +with a 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
      msnumber

      The amount of time to wait, in milliseconds.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the implicit wait timeout has been set.

      +

      pageLoadTimeout(ms)code »

      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.

      +
      Parameters
      msnumber

      The amount of time to wait, in milliseconds.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the timeout has been set.

      +

      setScriptTimeout(ms)code »

      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.

      +
      Parameters
      msnumber

      The amount of time to wait, in milliseconds.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the script timeout has been set.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebDriver_Window.html b/docs/class_webdriver_WebDriver_Window.html index 87e91b6..7d85985 100644 --- a/docs/class_webdriver_WebDriver_Window.html +++ b/docs/class_webdriver_WebDriver_Window.html @@ -1,10 +1,27 @@ -webdriver.WebDriver.Window

      Class webdriver.WebDriver.Window

      code »

      An interface for managing the current window.

      Constructor

      webdriver.WebDriver.Window ( driver )
      Parameters
      driver: !webdriver.WebDriver
      The parent driver.
      Show:

      Instance Methods

      Retrieves the window's current position, relative to the top left corner of - the screen.

      Returns
      A promise that will be resolved with the - window's position in the form of a {x:number, y:number} object literal.

      Retrieves the window's current size.

      Returns
      A promise that will be resolved with the - window's size in the form of a {width:number, height:number} object - literal.

      Maximizes the current window.

      Returns
      A promise that will be resolved when the - command has completed.

      Repositions the current window.

      Parameters
      x: number
      The desired horizontal position, relative to the left side - of the screen.
      y: number
      The desired vertical position, relative to the top of the - of the screen.
      Returns
      A promise that will be resolved when the - command has completed.

      Resizes the current window.

      Parameters
      width: number
      The desired window width.
      height: number
      The desired window height.
      Returns
      A promise that will be resolved when the - command has completed.

      Instance Properties

      \ No newline at end of file +WebDriver.Window

      class WebDriver.Window

      An interface for managing the current window.

      +

      new WebDriver.Window(driver)

      Parameters
      driverwebdriver.WebDriver

      The parent driver.

      +

      Instance Methods

      getPosition()code »

      Retrieves the window's current position, relative to the top left corner of +the screen.

      +
      Returns
      webdriver.promise.Promise<{x: number, y: number}>

      A promise that +will be resolved with the window's position in the form of a +{x:number, y:number} object literal.

      +

      getSize()code »

      Retrieves the window's current size.

      +
      Returns
      webdriver.promise.Promise<{height: number, width: number}>

      A +promise that will be resolved with the window's size in the form of a +{width:number, height:number} object literal.

      +

      maximize()code »

      Maximizes the current window.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the command has completed.

      +

      setPosition(x, y)code »

      Repositions the current window.

      +
      Parameters
      xnumber

      The desired horizontal position, relative to the left side +of the screen.

      +
      ynumber

      The desired vertical position, relative to the top of the +of the screen.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the command has completed.

      +

      setSize(width, height)code »

      Resizes the current window.

      +
      Parameters
      widthnumber

      The desired window width.

      +
      heightnumber

      The desired window height.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the command has completed.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebElement.html b/docs/class_webdriver_WebElement.html index e2522cb..283d113 100644 --- a/docs/class_webdriver_WebElement.html +++ b/docs/class_webdriver_WebElement.html @@ -1,252 +1,216 @@ -webdriver.WebElement

      Class webdriver.WebElement

      code »
      webdriver.promise.Promise
      -  └ webdriver.promise.Deferred
      -      └ webdriver.WebElement

      Represents a DOM element. WebElements can be found by searching from the - document root using a webdriver.WebDriver instance, or by searching - under another webdriver.WebElement: -

      
      -   driver.get('http://www.google.com');
      -   var searchForm = driver.findElement(By.tagName('form'));
      -   var searchBox = searchForm.findElement(By.name('q'));
      -   searchBox.sendKeys('webdriver');
      - 
      +WebElement

      class WebElement

      webdriver.Serializable<{ELEMENT: string}>
      +  └ webdriver.WebElement

      Represents a DOM element. WebElements can be found by searching from the +document root using a webdriver.WebDriver instance, or by searching +under another WebElement:

      +
      driver.get('http://www.google.com');
      +var searchForm = driver.findElement(By.tagName('form'));
      +var searchBox = searchForm.findElement(By.name('q'));
      +searchBox.sendKeys('webdriver');
      +
      +

      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.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');
      +});
      +
      +

      new WebElement(driver, id)

      Parameters
      driverwebdriver.WebDriver

      The parent WebDriver instance for this +element.

      +
      id(webdriver.promise.Promise<{ELEMENT: string}>|{ELEMENT: string})

      The server-assigned opaque ID for the +underlying DOM element.

      +

      Instance Methods

      clear()code »

      Schedules a command to clear the 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
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the element has been cleared.

      +

      click()code »

      Schedules a command to click on this element.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the click command has completed.

      +

      findElement(locator)code »

      Schedule a command to find a descendant of this element. If the element +cannot be found, a bot.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.

      +

      The search criteria for an element may be defined using one of the +factories in the 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'});
      +
      +

      You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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);
       
      - 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.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

      webdriver.WebElement ( driver, id )
      Parameters
      driver: !webdriver.WebDriver
      The parent WebDriver instance for this - element.
      id: !(string|webdriver.promise.Promise)
      Either the opaque ID for the - underlying DOM element assigned by the server, or a promise that will - resolve to that ID or another WebElement.
      Show:

      Instance Methods

      Defined in webdriver.WebElement

      Schedules a command to clear the 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.

      Schedules a command to click on this element.

      Returns
      A promise that will be resolved when - the click command has completed.

      Schedule a command to find a descendant of this element. If the element - cannot be found, a bot.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. - -

      The search criteria for an element may be defined using one of the - factories in the 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'});
      - 
      - -

      You may also provide a custom locator function, which takes as input - this WebDriver instance and returns a 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
      locator: !(webdriver.Locator|webdriver.By.Hash|Function)
      The - locator strategy to use when searching for the element.
      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.

      Schedules a command to find all of the descendants of this element that - match the given search criteria.

      Parameters
      locator: !(webdriver.Locator|webdriver.By.Hash|Function)
      The - locator strategy to use when searching for the elements.
      Returns
      A - promise that will resolve to an array of WebElements.

      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: -

        -
      • "class" -
      • "readonly" -
      Parameters
      attributeName: string
      The name of the attribute to query.
      Returns
      A promise that will be resolved with the - attribute's value. The returned value will always be either a string or - null.

      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)). -

      - Warning: the value returned will be as the browser interprets it, so - it may be tricky to form a proper assertion.

      Parameters
      cssStyleProperty: string
      The name of the CSS style property to look - up.
      Returns
      A promise that will be resolved with the - requested CSS value.
      Returns
      The parent driver for this instance.

      Schedules a command to retrieve the inner HTML of this element.

      Returns
      A promise that will be resolved with the - element's inner HTML.

      Schedules a command to compute the location of this element in page space.

      Returns
      A promise that will be resolved to the - element's location as a {x:number, y:number} object.

      Schedules a command to retrieve the outer HTML of this element.

      Returns
      A promise that will be resolved with - the element's outer HTML.

      Schedules a command to compute the size of this element's bounding box, in - pixels.

      Returns
      A promise that will be resolved with the - element's size as a {width:number, height:number} object.

      Schedules a command to query for the tag/node name of this element.

      Returns
      A promise that will be resolved with the - element's tag name.

      Get the visible (i.e. not hidden by CSS) innerText of this element, including - sub-elements, without any leading or trailing whitespace.

      Returns
      A promise that will be resolved with the - element's visible text.

      Schedules a command to test whether this element is currently displayed.

      Returns
      A promise that will be resolved with - whether this element is currently visible on the page.

      Schedules a command to test if there is at least one descendant of this - element that matches the given search criteria.

      Parameters
      locator: !(webdriver.Locator|webdriver.By.Hash|Function)
      The - locator strategy to use when searching for the element.
      Returns
      A promise that will be - resolved with whether an element could be located on the page.

      Schedules a command to query whether the DOM element represented by this - instance is enabled, as dicted by the disabled attribute.

      Returns
      A promise that will be resolved with - whether this element is currently enabled.

      Schedules a command to query whether this element is selected.

      Returns
      A promise that will be resolved with - whether this element is currently selected.
      code »schedule_ ( command, description )!webdriver.promise.Promise

      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.

      Parameters
      command: !webdriver.Command
      The command to schedule.
      description: string
      A description of the command for debugging.
      Returns
      A promise that will be resolved with - the command result.

      Schedules a command to type a sequence on the DOM element represented by this - instance. -

      - Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is - processed in the keysequence, that key state is toggled until one of the - following occurs: -

        -
      • The modifier key is encountered again in the sequence. At this point the - state of the key is toggled (along with the appropriate keyup/down events). -
      • -
      • The 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");
        - 
      • -
      • The end of the keysequence is encountered. When there are no more keys - to type, all depressed modifier keys are released (with accompanying keyup - events). -
      • -
      - 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.
      Parameters
      var_args: ...string
      The sequence of keys to - type. All arguments will be joined into a single sequence (var_args is - permitted for convenience).
      Returns
      A promise that will be resolved when all - keys have been typed.

      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.

      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.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will - be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.
      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a - promise and not a reference to this deferred, this instance will wait for - it before resolving.

      Parameters
      opt_value: *=
      The resolved value.
      code »reject ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will - be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise

      code »addBoth ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenFinally() instead.

      Registers a function to be invoked when this promise is either rejected or - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call when this promise is - either resolved or rejected. The function should expect a single - argument: the resolved value or rejection error.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallback ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      Registers a function to be invoked when this promise is successfully - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallbacks ( callback, errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      An alias for webdriver.promise.Promise.prototype.then that permits - the scope of the invoked function to be specified. This function is provided - for backwards compatibility with the Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addErrback ( errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenCatch() instead.

      Registers a function to be invoked when this promise is rejected. - This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »cancel ( reason )

      Cancels the computation of this promise's value, rejecting the promise in the - process.

      Parameters
      reason: *
      The reason this promise is being cancelled. If not an - Error, one will be created using the value's string - representation.
      Returns
      Whether this promise's value is still being computed.
      code »then ( opt_callback, opt_errback )!webdriver.promise.Promise

      Registers listeners for when this instance is resolved. This function most - overridden by subtypes.

      Parameters
      opt_callback: Function=
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_errback: Function=
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      Registers a listener for when this promise is rejected. This is synonymous - with the 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
      errback: !Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      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 finally clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } finally {
      -     cleanUp();
      -   }
      -
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenFinally(cleanUp);
      - 
      - - Note: similar to the 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
      callback

      Instance Properties

      Defined in webdriver.WebElement

      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.

      Defined in webdriver.promise.Deferred

      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. - -

      This class is based on the Promise/A proposal from CommonJS. Additional - functions are provided for API compatibility with Dojo Deferred objects.

      Static Functions

      Compares to WebElements for equality.

      Parameters
      a: !webdriver.WebElement
      A WebElement.
      b: !webdriver.WebElement
      A WebElement.
      Returns
      A promise that will be resolved to - whether the two WebElements are equal.

      Static Properties

      The property key used in the wire protocol to indicate that a JSON object - contains the ID of a WebElement.

      \ No newline at end of file +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
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The +locator strategy to use when searching for the element.

      +
      Returns
      webdriver.WebElement

      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.

      +

      findElements(locator)code »

      Schedules a command to find all of the descendants of this element that +match the given search criteria.

      +
      Parameters
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The +locator strategy to use when searching for the elements.

      +
      Returns
      webdriver.promise.Promise<Array<webdriver.WebElement>>

      A +promise that will resolve to an array of WebElements.

      +

      getAttribute(attributeName)code »

      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:

      +
      • "class"
      • "readonly"
      +
      Parameters
      attributeNamestring

      The name of the attribute to query.

      +
      Returns
      webdriver.promise.Promise<?string>

      A promise that will be +resolved with the attribute's value. The returned value will always be +either a string or null.

      +

      getCssValue(cssStyleProperty)code »

      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)).

      +

      Warning: the value returned will be as the browser interprets it, so +it may be tricky to form a proper assertion.

      +
      Parameters
      cssStylePropertystring

      The name of the CSS style property to look +up.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the requested CSS value.

      +

      getDriver()code »

      Returns
      webdriver.WebDriver

      The parent driver for this instance.

      +

      getId()code »

      Returns
      webdriver.promise.Promise<{ELEMENT: string}>

      A promise +that resolves to this element's JSON representation as defined by the +WebDriver wire protocol.

      +

      getInnerHtml()code »

      Schedules a command to retrieve the inner HTML of this element.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's inner HTML.

      +

      getLocation()code »

      Schedules a command to compute the location of this element in page space.

      +
      Returns
      webdriver.promise.Promise<{x: number, y: number}>

      A promise that +will be resolved to the element's location as a +{x:number, y:number} object.

      +

      getOuterHtml()code »

      Schedules a command to retrieve the outer HTML of this element.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's outer HTML.

      +

      getRawId()code »

      Returns the raw ID string ID for this element.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that resolves to this +element's raw ID as a string value.

      +

      getSize()code »

      Schedules a command to compute the size of this element's bounding box, in +pixels.

      +
      Returns
      webdriver.promise.Promise<{height: number, width: number}>

      A +promise that will be resolved with the element's size as a +{width:number, height:number} object.

      +

      getTagName()code »

      Schedules a command to query for the tag/node name of this element.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's tag name.

      +

      getText()code »

      Get the visible (i.e. not hidden by CSS) innerText of this element, including +sub-elements, without any leading or trailing whitespace.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's visible text.

      +

      isDisplayed()code »

      Schedules a command to test whether this element is currently displayed.

      +
      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether this element is currently visible on the page.

      +

      isElementPresent(locator)code »

      Schedules a command to test if there is at least one descendant of this +element that matches the given search criteria.

      +
      Parameters
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The +locator strategy to use when searching for the element.

      +
      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether an element could be located on the page.

      +

      isEnabled()code »

      Schedules a command to query whether the DOM element represented by this +instance is enabled, as dicted by the disabled attribute.

      +
      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether this element is currently enabled.

      +

      isSelected()code »

      Schedules a command to query whether this element is selected.

      +
      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether this element is currently selected.

      +

      sendKeys(var_args)code »

      Schedules a command to type a sequence on the DOM element represented by this +instance.

      +

      Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is +processed in the keysequence, that key state is toggled until one of the +following occurs:

      +
      • +

        The modifier key is encountered again in the sequence. At this point the +state of the key is toggled (along with the appropriate keyup/down events).

        +
      • +

        The 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");
        +
        +
      • +

        The end of the keysequence is encountered. When there are no more keys +to type, all depressed modifier keys are released (with accompanying keyup +events).

        +
      +

      If this element is a file input (<input type="file">), the +specified key sequence should specify the path to the file to attach to +the element. This is analgous to the user clicking "Browse..." and entering +the path into the file select dialog.

      +
      var form = driver.findElement(By.css('form'));
      +var element = form.findElement(By.css('input[type=file]'));
      +element.sendKeys('/path/to/file.txt');
      +form.submit();
      +
      +

      For uploads to function correctly, the entered path must reference a file +on the browser's machine, not the local machine running this script. When +running against a remote Selenium server, a webdriver.FileDetector +may be used to transparently copy files to the remote machine before +attempting to upload them in the browser.

      +

      Note: On browsers where native keyboard events are not 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.

      +
      Parameters
      var_args...(string|webdriver.promise.Promise<string>)

      The sequence +of keys to type. All arguments will be joined into a single sequence.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when all keys have been typed.

      +

      serialize()code »

      Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

      +

      Overrides: webdriver.Serializable

      Returns
      (webdriver.WebElement.Id|IThenable<webdriver.WebElement.Id>)

      This instance's serialized wire format.

      +

      submit()code »

      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.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the form has been submitted.

      +

      Static Functions

      WebElement.equals(a, b)code »

      Compares to WebElements for equality.

      +
      Parameters
      awebdriver.WebElement

      A WebElement.

      +
      bwebdriver.WebElement

      A WebElement.

      +
      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved to whether the two WebElements are equal.

      +

      Static Properties

      WebElement.ELEMENT_KEYstring

      The property key used in the wire protocol to indicate that a JSON object +contains the ID of a WebElement.

      +

      Type Definitions

      WebElement.Id{ELEMENT: string}

      Wire protocol definition of a WebElement ID.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_WebElementPromise.html b/docs/class_webdriver_WebElementPromise.html new file mode 100644 index 0000000..077bf25 --- /dev/null +++ b/docs/class_webdriver_WebElementPromise.html @@ -0,0 +1,267 @@ +WebElementPromise

      class WebElementPromise

      final
      webdriver.Serializable<{ELEMENT: string}>
      +  └ webdriver.WebElement
      +      └ webdriver.WebElementPromise
      All implemented interfaces
      IThenable<T>
      webdriver.promise.Thenable<webdriver.WebElement>

      WebElementPromise is a promise that will be fulfilled with a WebElement. +This serves as a forward proxy on WebElement, allowing calls to be +scheduled without directly on this instance before the underlying +WebElement has been fulfilled. In other words, the following two statements +are equivalent:

      +
      driver.findElement({id: 'my-button'}).click();
      +driver.findElement({id: 'my-button'}).then(function(el) {
      +  return el.click();
      +});
      +
      +

      new WebElementPromise(driver, el)

      Parameters
      driverwebdriver.WebDriver

      The parent WebDriver instance for this +element.

      +
      elwebdriver.promise.Promise<webdriver.WebElement>

      A promise +that will resolve to the promised element.

      +

      Instance Methods

      cancel(opt_reason)code »

      Cancels the computation of this promise's value, rejecting the promise in +the process. This method is a no-op if the promise has already been +resolved.

      +

      Specified by: webdriver.promise.Thenable

      Parameters
      opt_reason?(string|webdriver.promise.CancellationError)=

      The reason this +promise is being cancelled.

      +

      clear()code »

      Schedules a command to clear the value of this element. This command +has no effect if the underlying DOM element is neither a text INPUT element +nor a TEXTAREA element.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the element has been cleared.

      +

      click()code »

      Schedules a command to click on this element.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the click command has completed.

      +

      findElement(locator)code »

      Schedule a command to find a descendant of this element. If the element +cannot be found, a bot.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.

      +

      The search criteria for an element may be defined using one of the +factories in the 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'});
      +
      +

      You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
      +  });
      +}
      +
      +

      Defined by: webdriver.WebElement

      Parameters
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The +locator strategy to use when searching for the element.

      +
      Returns
      webdriver.WebElement

      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.

      +

      findElements(locator)code »

      Schedules a command to find all of the descendants of this element that +match the given search criteria.

      +

      Defined by: webdriver.WebElement

      Parameters
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The +locator strategy to use when searching for the elements.

      +
      Returns
      webdriver.promise.Promise<Array<webdriver.WebElement>>

      A +promise that will resolve to an array of WebElements.

      +

      getAttribute(attributeName)code »

      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:

      +
      • "class"
      • "readonly"
      +

      Defined by: webdriver.WebElement

      Parameters
      attributeNamestring

      The name of the attribute to query.

      +
      Returns
      webdriver.promise.Promise<?string>

      A promise that will be +resolved with the attribute's value. The returned value will always be +either a string or null.

      +

      getCssValue(cssStyleProperty)code »

      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)).

      +

      Warning: the value returned will be as the browser interprets it, so +it may be tricky to form a proper assertion.

      +

      Defined by: webdriver.WebElement

      Parameters
      cssStylePropertystring

      The name of the CSS style property to look +up.

      +
      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the requested CSS value.

      +

      getDriver()code »

      Defined by: webdriver.WebElement

      Returns
      webdriver.WebDriver

      The parent driver for this instance.

      +

      getId()code »

      Defers returning the element ID until the wrapped WebElement has been +resolved.

      +

      Overrides: webdriver.WebElement

      Returns
      webdriver.promise.Promise<{ELEMENT: string}>

      A promise +that resolves to this element's JSON representation as defined by the +WebDriver wire protocol.

      +

      getInnerHtml()code »

      Schedules a command to retrieve the inner HTML of this element.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's inner HTML.

      +

      getLocation()code »

      Schedules a command to compute the location of this element in page space.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<{x: number, y: number}>

      A promise that +will be resolved to the element's location as a +{x:number, y:number} object.

      +

      getOuterHtml()code »

      Schedules a command to retrieve the outer HTML of this element.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's outer HTML.

      +

      getRawId()code »

      Returns the raw ID string ID for this element.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<string>

      A promise that resolves to this +element's raw ID as a string value.

      +

      getSize()code »

      Schedules a command to compute the size of this element's bounding box, in +pixels.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<{height: number, width: number}>

      A +promise that will be resolved with the element's size as a +{width:number, height:number} object.

      +

      getTagName()code »

      Schedules a command to query for the tag/node name of this element.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's tag name.

      +

      getText()code »

      Get the visible (i.e. not hidden by CSS) innerText of this element, including +sub-elements, without any leading or trailing whitespace.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<string>

      A promise that will be +resolved with the element's visible text.

      +

      isDisplayed()code »

      Schedules a command to test whether this element is currently displayed.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether this element is currently visible on the page.

      +

      isElementPresent(locator)code »

      Schedules a command to test if there is at least one descendant of this +element that matches the given search criteria.

      +

      Defined by: webdriver.WebElement

      Parameters
      locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

      The +locator strategy to use when searching for the element.

      +
      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether an element could be located on the page.

      +

      isEnabled()code »

      Schedules a command to query whether the DOM element represented by this +instance is enabled, as dicted by the disabled attribute.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether this element is currently enabled.

      +

      isPending()code »

      Specified by: webdriver.promise.Thenable

      Returns
      boolean

      Whether this promise's value is still being computed.

      +

      isSelected()code »

      Schedules a command to query whether this element is selected.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<boolean>

      A promise that will be +resolved with whether this element is currently selected.

      +

      sendKeys(var_args)code »

      Schedules a command to type a sequence on the DOM element represented by this +instance.

      +

      Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is +processed in the keysequence, that key state is toggled until one of the +following occurs:

      +
      • +

        The modifier key is encountered again in the sequence. At this point the +state of the key is toggled (along with the appropriate keyup/down events).

        +
      • +

        The 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");
        +
        +
      • +

        The end of the keysequence is encountered. When there are no more keys +to type, all depressed modifier keys are released (with accompanying keyup +events).

        +
      +

      If this element is a file input (<input type="file">), the +specified key sequence should specify the path to the file to attach to +the element. This is analgous to the user clicking "Browse..." and entering +the path into the file select dialog.

      +
      var form = driver.findElement(By.css('form'));
      +var element = form.findElement(By.css('input[type=file]'));
      +element.sendKeys('/path/to/file.txt');
      +form.submit();
      +
      +

      For uploads to function correctly, the entered path must reference a file +on the browser's machine, not the local machine running this script. When +running against a remote Selenium server, a webdriver.FileDetector +may be used to transparently copy files to the remote machine before +attempting to upload them in the browser.

      +

      Note: On browsers where native keyboard events are not 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.

      +

      Defined by: webdriver.WebElement

      Parameters
      var_args...(string|webdriver.promise.Promise<string>)

      The sequence +of keys to type. All arguments will be joined into a single sequence.

      +
      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when all keys have been typed.

      +

      serialize()code »

      Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

      +

      Defined by: webdriver.WebElement
      Overrides: webdriver.Serializable

      Returns
      (webdriver.WebElement.Id|IThenable<webdriver.WebElement.Id>)

      This instance's serialized wire format.

      +

      submit()code »

      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.

      +

      Defined by: webdriver.WebElement

      Returns
      webdriver.promise.Promise<undefined>

      A promise that will be resolved +when the form has been submitted.

      +

      then(opt_callback, opt_errback)code »

      Registers listeners for when this instance is resolved.

      +

      Specified by: webdriver.promise.Thenable, IThenable

      Parameters
      opt_callback?function(T): (R|IThenable<R>)=

      The +function to call if this promise is successfully resolved. The function +should expect a single argument: the promise's resolved value.

      +
      opt_errback?function(*): (R|IThenable<R>)=

      The function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenCatch(errback)code »

      Registers a listener for when this promise is rejected. This is synonymous +with the 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);
      +});
      +
      +

      Specified by: webdriver.promise.Thenable

      Parameters
      errbackfunction(*): (R|IThenable<R>)

      The +function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenFinally(callback)code »

      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 finally clause in a synchronous API:

      +
      // Synchronous API:
      +try {
      +  doSynchronousWork();
      +} finally {
      +  cleanUp();
      +}
      +
      +// Asynchronous promise API:
      +doAsynchronousWork().thenFinally(cleanUp);
      +
      +

      Note: similar to the 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
      +}
      +
      +promise.rejected(Error('one'))
      +    .thenFinally(function() {
      +      throw Error('two');  // Hides Error: one
      +    });
      +
      +

      Specified by: webdriver.promise.Thenable

      Parameters
      callbackfunction(): (R|IThenable<R>)

      The function +to call when this promise is resolved.

      +
      Returns
      webdriver.promise.Promise

      A promise that will be fulfilled +with the callback result.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_http_CorsClient.html b/docs/class_webdriver_http_CorsClient.html index 7a7b1f1..cbffd87 100644 --- a/docs/class_webdriver_http_CorsClient.html +++ b/docs/class_webdriver_http_CorsClient.html @@ -30,4 +30,4 @@ onerror handler, but without the corresponding response text returned by the server. This renders IE and Opera incapable of handling command failures in the standard JSON protocol. -

      Constructor

      webdriver.http.CorsClient ( url )
      Parameters
      url: string
      URL for the WebDriver server to send commands to.
      Show:

      Instance Methods

      code »send ( request, callback )
      Parameters
      request
      callback

      Instance Properties

      Static Functions

      Tests whether the current environment supports cross-origin resource sharing.

      Returns
      Whether cross-origin resource sharing is supported.

      Static Properties

      Resource URL to send commands to on the server.

      \ No newline at end of file +

      Constructor

      webdriver.http.CorsClient ( url )
      Parameters
      url: string
      URL for the WebDriver server to send commands to.
      Show:

      Instance Methods

      code »send ( request, callback )
      Parameters
      request
      callback

      Instance Properties

      Static Functions

      Tests whether the current environment supports cross-origin resource sharing.

      Returns
      Whether cross-origin resource sharing is supported.

      Static Properties

      Resource URL to send commands to on the server.

      \ No newline at end of file diff --git a/docs/class_webdriver_http_Executor.html b/docs/class_webdriver_http_Executor.html index 1e656a3..09b05c9 100644 --- a/docs/class_webdriver_http_Executor.html +++ b/docs/class_webdriver_http_Executor.html @@ -1,8 +1,22 @@ -webdriver.http.Executor

      Class webdriver.http.Executor

      code »
      All implemented interfaces:
      webdriver.CommandExecutor

      A command executor that communicates with a server using the WebDriver - command protocol.

      Constructor

      webdriver.http.Executor ( client )
      Parameters
      client: !webdriver.http.Client
      The client to use when sending - requests to the server.
      Show:

      Instance Methods

      code »execute ( command, callback )
      Parameters
      command
      callback

      Instance Properties

      Client used to communicate with the server.

      Static Functions

      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.

      Parameters
      path: string
      The original resource path.
      parameters: !Object
      The parameters object to splice into - the path.
      Returns
      The modified path.

      Callback used to parse webdriver.http.Response objects from a - webdriver.http.Client.

      Parameters
      httpResponse: !webdriver.http.Response
      The HTTP response to parse.
      Returns
      The parsed response.

      Static Properties

      Maps command names to resource locator.

      \ No newline at end of file +Executor

      class Executor

      All implemented interfaces
      webdriver.CommandExecutor

      A command executor that communicates with a server using the WebDriver +command protocol.

      +

      new Executor(client)

      Parameters
      clientwebdriver.http.Client

      The client to use when sending +requests to the server.

      +

      Instance Methods

      defineCommand(name, method, path)code »

      Defines a new command for use with this executor. When a command is sent, +the path will be preprocessed using the command's parameters; any +path segments prefixed with ":" will be replaced by the parameter of the +same name. For example, given "/person/:name" and the parameters +"{name: 'Bob'}", the final command path will be "/person/Bob".

      +
      Parameters
      namestring

      The command name.

      +
      methodstring

      The HTTP method to use when sending this command.

      +
      pathstring

      The path to send the command to, relative to +the WebDriver server's command root and of the form +"/path/:variable/segment".

      +

      execute(command, callback)code »

      Executes the given 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.

      +

      Specified by: webdriver.CommandExecutor

      Parameters
      commandwebdriver.Command

      The command to execute.

      +
      callbackfunction(Error, {status: number, value: *}=): ?

      the function +to invoke when the command response is ready.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_http_Request.html b/docs/class_webdriver_http_Request.html index 2c4efb5..8f6392f 100644 --- a/docs/class_webdriver_http_Request.html +++ b/docs/class_webdriver_http_Request.html @@ -1,4 +1,12 @@ -webdriver.http.Request

      Class webdriver.http.Request

      code »

      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 - webdriver.http.Client's responsibility to build the full URL for the - final request.

      Constructor

      webdriver.http.Request ( method, path, opt_data )
      Parameters
      method: string
      The HTTP method to use for the request.
      path: string
      Path on the server to send the request to.
      opt_data: Object=
      This request's JSON data.
      Show:

      Instance Methods

      code »toString ( )string

      Instance Properties

      This request's body.

      The headers to send with the request.

      The HTTP method to use for the request.

      The path on the server to send the request to.

      \ No newline at end of file +Request

      class Request

      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 +webdriver.http.Client's responsibility to build the full URL for the +final request.

      +

      new Request(method, path, opt_data)

      Parameters
      methodstring

      The HTTP method to use for the request.

      +
      pathstring

      Path on the server to send the request to.

      +
      opt_data?Object=

      This request's JSON data.

      +

      Instance Methods

      toString()code »

      Returns
      string

      Instance Properties

      dataObject

      This request's body.

      +
      headersObject<?, (string|number)>

      The headers to send with the request.

      +
      methodstring

      The HTTP method to use for the request.

      +
      pathstring

      The path on the server to send the request to.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_http_Response.html b/docs/class_webdriver_http_Response.html index c39d270..785057a 100644 --- a/docs/class_webdriver_http_Response.html +++ b/docs/class_webdriver_http_Response.html @@ -1,3 +1,13 @@ -webdriver.http.Response

      Class webdriver.http.Response

      code »

      Represents a HTTP response.

      Constructor

      webdriver.http.Response ( status, headers, body )
      Parameters
      status: number
      The response code.
      headers: !Object.<string>
      The response headers. All header - names will be converted to lowercase strings for consistent lookups.
      body: string
      The response body.
      Show:

      Instance Methods

      code »toString ( )string

      Instance Properties

      The response body.

      The response body.

      The HTTP response code.

      Static Functions

      Builds a webdriver.http.Response from a XMLHttpRequest or - XDomainRequest response object.

      Parameters
      xhr: !(XDomainRequest|XMLHttpRequest)
      The request to parse.
      Returns
      The parsed response.
      \ No newline at end of file +Response

      class Response

      Represents a HTTP response.

      +

      new Response(status, headers, body)

      Parameters
      statusnumber

      The response code.

      +
      headersObject<?, string>

      The response headers. All header +names will be converted to lowercase strings for consistent lookups.

      +
      bodystring

      The response body.

      +

      Instance Methods

      toString()code »

      Returns
      string

      Instance Properties

      bodystring

      The response body.

      +
      headersObject<?, string>

      The response body.

      +
      statusnumber

      The HTTP response code.

      +

      Static Functions

      Response.fromXmlHttpRequest(xhr)code »

      Builds a webdriver.http.Response from a XMLHttpRequest or +XDomainRequest response object.

      +
      Parameters
      xhr(XDomainRequest|XMLHttpRequest)

      The request to parse.

      +
      Returns
      webdriver.http.Response

      The parsed response.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_http_XhrClient.html b/docs/class_webdriver_http_XhrClient.html index d53dfaa..1ed338f 100644 --- a/docs/class_webdriver_http_XhrClient.html +++ b/docs/class_webdriver_http_XhrClient.html @@ -1 +1 @@ -webdriver.http.XhrClient

      Class webdriver.http.XhrClient

      code »
      All implemented interfaces:
      webdriver.http.Client

      A HTTP client that sends requests using XMLHttpRequests.

      Constructor

      webdriver.http.XhrClient ( url )
      Parameters
      url: string
      URL for the WebDriver server to send commands to.
      Show:

      Instance Methods

      code »send ( request, callback )
      Parameters
      request
      callback

      Instance Properties

      \ No newline at end of file +webdriver.http.XhrClient

      Class webdriver.http.XhrClient

      code »
      All implemented interfaces:
      webdriver.http.Client

      A HTTP client that sends requests using XMLHttpRequests.

      Constructor

      webdriver.http.XhrClient ( url )
      Parameters
      url: string
      URL for the WebDriver server to send commands to.
      Show:

      Instance Methods

      code »send ( request, callback )
      Parameters
      request
      callback

      Instance Properties

      \ No newline at end of file diff --git a/docs/class_webdriver_logging_Entry.html b/docs/class_webdriver_logging_Entry.html index c6f0822..acb8ad9 100644 --- a/docs/class_webdriver_logging_Entry.html +++ b/docs/class_webdriver_logging_Entry.html @@ -1,4 +1,15 @@ -webdriver.logging.Entry

      Class webdriver.logging.Entry

      code »

      A single log entry.

      Constructor

      webdriver.logging.Entry ( level, message, opt_timestamp, opt_type )
      Parameters
      level: (!webdriver.logging.Level|string)
      The entry level.
      message: string
      The log message.
      opt_timestamp: number=
      The time this entry was generated, in - milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the - current time will be used.
      opt_type: string=
      The log type, if known.
      Show:

      Instance Methods

      code »toJSON ( ){level: string, message: string, timestamp: number, type: string}
      Returns
      The JSON representation of this entry.

      Instance Properties

      Static Functions

      Converts a goog.debug.LogRecord into a - webdriver.logging.Entry.

      Parameters
      logRecord: !goog.debug.LogRecord
      The record to convert.
      opt_type: string=
      The log type.
      Returns
      The converted entry.
      \ No newline at end of file +Entry

      class Entry

      A single log entry recorded by a WebDriver component, such as a remote +WebDriver server.

      +

      new Entry(level, message, opt_timestamp, opt_type)

      Parameters
      level(webdriver.logging.Level|string)

      The entry level.

      +
      messagestring

      The log message.

      +
      opt_timestampnumber=

      The time this entry was generated, in +milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the +current time will be used.

      +
      opt_typestring=

      The log type, if known.

      +

      Instance Methods

      toJSON(arg0)code »

      Parameters
      arg0string=
      Returns
      {level: string, message: string, timestamp: number, type: string}

      The JSON representation of this entry.

      +

      Instance Properties

      messagestring
      No description.
      timestampnumber
      No description.
      typestring
      No description.

      Static Functions

      Entry.fromClosureLogRecord(logRecord, opt_type)code »

      Converts a goog.debug.LogRecord into a +webdriver.logging.Entry.

      +
      Parameters
      logRecordwebdriver.logging.LogRecord

      The record to convert.

      +
      opt_typestring=

      The log type.

      +
      Returns
      webdriver.logging.Entry

      The converted entry.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_logging_Level.html b/docs/class_webdriver_logging_Level.html new file mode 100644 index 0000000..1671f7e --- /dev/null +++ b/docs/class_webdriver_logging_Level.html @@ -0,0 +1,55 @@ +Level

      class Level

      The Level class defines a set of standard logging levels that +can be used to control logging output. The logging Level objects +are ordered and are specified by ordered integers. Enabling logging +at a given level also enables logging at all higher levels.

      +

      +Clients should normally use the predefined Level constants such +as Level.SEVERE. +

      +The levels in descending order are: +

      • SEVERE (highest value) +
      • WARNING +
      • INFO +
      • CONFIG +
      • FINE +
      • FINER +
      • FINEST (lowest value) +
      +In addition there is a level OFF that can be used to turn +off logging, and a level ALL that can be used to enable +logging of all messages. +

      new Level(name, value)

      Parameters
      namestring

      The name of the level.

      +
      valuenumber

      The numeric value of the level.

      +

      Instance Methods

      toString()code »

      Returns
      string

      String representation of the logger level.

      +

      Instance Properties

      namestring

      The name of the level

      +
      valuenumber

      The numeric value of the level

      +

      Static Functions

      Level.getPredefinedLevel(name)code »

      Gets the predefined level with the given name.

      +
      Parameters
      namestring

      The name of the level.

      +
      Returns
      webdriver.logging.Level

      The level, or null if none found.

      +

      Level.getPredefinedLevelByValue(value)code »

      Gets the highest predefined level <= #value.

      +
      Parameters
      valuenumber

      Level value.

      +
      Returns
      webdriver.logging.Level

      The level, or null if none found.

      +

      Static Properties

      Level.ALLwebdriver.logging.Level

      ALL indicates that all messages should be logged. +This level is initialized to 0.

      +
      Level.CONFIGwebdriver.logging.Level

      CONFIG is a message level for static configuration messages. +This level is initialized to 700.

      +
      Level.DEBUGwebdriver.logging.Level

      DEBUG is a message level for debugging messages and has the same log level +as the Logger.Level.CONFIG message level.

      +
      Level.FINEwebdriver.logging.Level

      FINE is a message level providing tracing information. +This level is initialized to 500.

      +
      Level.FINERwebdriver.logging.Level

      FINER indicates a fairly detailed tracing message. +This level is initialized to 400.

      +
      Level.FINESTwebdriver.logging.Level

      FINEST indicates a highly detailed tracing message. +This level is initialized to 300.

      +
      Level.INFOwebdriver.logging.Level

      INFO is a message level for informational messages. +This level is initialized to 800.

      +
      Level.OFFwebdriver.logging.Level

      OFF is a special level that can be used to turn off logging. +This level is initialized to Infinity.

      +
      Level.SEVEREwebdriver.logging.Level

      SEVERE is a message level indicating a serious failure. +This level is initialized to 1000.

      +
      Level.SHOUTwebdriver.logging.Level

      SHOUT is a message level for extra debugging loudness. +This level is initialized to 1200.

      +
      Level.WARNINGwebdriver.logging.Level

      WARNING is a message level indicating a potential problem. +This level is initialized to 900.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_logging_LogRecord.html b/docs/class_webdriver_logging_LogRecord.html new file mode 100644 index 0000000..37154d7 --- /dev/null +++ b/docs/class_webdriver_logging_LogRecord.html @@ -0,0 +1,45 @@ +LogRecord

      class LogRecord

      LogRecord objects are used to pass logging requests between +the logging framework and individual log Handlers.

      +

      new LogRecord(level, msg, loggerName, opt_time, opt_sequenceNumber)

      Parameters
      levelwebdriver.logging.Level

      One of the level identifiers.

      +
      msgstring

      The string message.

      +
      loggerNamestring

      The name of the source logger.

      +
      opt_timenumber=

      Time this log record was created if other than now. +If 0, we use #goog.now.

      +
      opt_sequenceNumbernumber=

      Sequence number of this log record. This +should only be passed in when restoring a log record from persistence.

      +

      Instance Methods

      getException()code »

      Get the exception that is part of the log record.

      +
      Returns
      Object

      the exception.

      +

      getLevel()code »

      Get the logging message level, for example Level.SEVERE.

      +
      Returns
      webdriver.logging.Level

      the logging message level.

      +

      getLoggerName()code »

      Get the source Logger's name.

      +
      Returns
      string

      source logger name (may be null).

      +

      getMessage()code »

      Get the "raw" log message, before localization or formatting.

      +
      Returns
      string

      the raw message string.

      +

      getMillis()code »

      Get event time in milliseconds since 1970.

      +
      Returns
      number

      event time in millis since 1970.

      +

      getSequenceNumber()code »

      Get the sequence number.

      +

      +Sequence numbers are normally assigned in the LogRecord +constructor, which assigns unique sequence numbers to +each new LogRecord in increasing order. +

      Returns
      number

      the sequence number.

      +

      reset(level, msg, loggerName, opt_time, opt_sequenceNumber)code »

      Sets all fields of the log record.

      +
      Parameters
      levelwebdriver.logging.Level

      One of the level identifiers.

      +
      msgstring

      The string message.

      +
      loggerNamestring

      The name of the source logger.

      +
      opt_timenumber=

      Time this log record was created if other than now. +If 0, we use #goog.now.

      +
      opt_sequenceNumbernumber=

      Sequence number of this log record. This +should only be passed in when restoring a log record from persistence.

      +

      setException(exception)code »

      Set the exception that is part of the log record.

      +
      Parameters
      exceptionObject

      the exception.

      +

      setLevel(level)code »

      Set the logging message level, for example Level.SEVERE.

      +
      Parameters
      levelwebdriver.logging.Level

      the logging message level.

      +

      setLoggerName(loggerName)code »

      Get the source Logger's name.

      +
      Parameters
      loggerNamestring

      source logger name (may be null).

      +

      setMessage(msg)code »

      Set the "raw" log message, before localization or formatting.

      +
      Parameters
      msgstring

      the raw message string.

      +

      setMillis(time)code »

      Set event time in milliseconds since 1970.

      +
      Parameters
      timenumber

      event time in millis since 1970.

      +

      Compiler Constants

      LogRecord.ENABLE_SEQUENCE_NUMBERSboolean

      Whether to enable log sequence numbers.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_logging_Logger.html b/docs/class_webdriver_logging_Logger.html new file mode 100644 index 0000000..f500dde --- /dev/null +++ b/docs/class_webdriver_logging_Logger.html @@ -0,0 +1,119 @@ +Logger

      class Logger

      The Logger is an object used for logging debug messages. Loggers are +normally named, using a hierarchical dot-separated namespace. Logger names +can be arbitrary strings, but they should normally be based on the package +name or class name of the logged component, such as goog.net.BrowserChannel.

      +

      The Logger object is loosely based on the java class +java.util.logging.Logger. It supports different levels of filtering for +different loggers.

      +

      The logger object should never be instantiated by application code. It +should always use the goog.debug.Logger.getLogger function.

      +

      new Logger(name)

      Parameters
      namestring

      The name of the Logger.

      +

      Instance Methods

      addHandler(handler)code »

      Adds a handler to the logger. This doesn't use the event system because +we want to be able to add logging to the event system.

      +
      Parameters
      handlerFunction

      Handler function to add.

      +

      config(msg, opt_exception)code »

      Logs a message at the Logger.Level.CONFIG level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      fine(msg, opt_exception)code »

      Logs a message at the Logger.Level.FINE level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      finer(msg, opt_exception)code »

      Logs a message at the Logger.Level.FINER level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      finest(msg, opt_exception)code »

      Logs a message at the Logger.Level.FINEST level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      getChildren()code »

      Returns the children of this logger as a map of the child name to the logger.

      +
      Returns
      Object

      The map where the keys are the child leaf names and the +values are the Logger objects.

      +

      getEffectiveLevel()code »

      Returns the effective level of the logger based on its ancestors' levels.

      +
      Returns
      webdriver.logging.Level

      The level.

      +

      getLevel()code »

      Gets the log level specifying which message levels will be logged by this +logger. Message levels lower than this value will be discarded. +The level value Level.OFF can be used to turn off logging. If the level +is null, it means that this node should inherit its level from its nearest +ancestor with a specific (non-null) level value.

      +
      Returns
      webdriver.logging.Level

      The level.

      +

      getLogRecord(level, msg, opt_exception)code »

      Creates a new log record and adds the exception (if present) to it.

      +
      Parameters
      levelwebdriver.logging.Level

      One of the level identifiers.

      +
      msgstring

      The string message.

      +
      opt_exception?Object=

      An exception associated with the +message.

      +
      Returns
      webdriver.logging.LogRecord

      A log record.

      +

      getName()code »

      Gets the name of this logger.

      +
      Returns
      string

      The name of this logger.

      +

      getParent()code »

      Returns the parent of this logger.

      +
      Returns
      webdriver.logging.Logger

      The parent logger or null if this is the root.

      +

      info(msg, opt_exception)code »

      Logs a message at the Logger.Level.INFO level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      isLoggable(level)code »

      Checks if a message of the given level would actually be logged by this +logger. This check is based on the Loggers effective level, which may be +inherited from its parent.

      +
      Parameters
      levelwebdriver.logging.Level

      The level to check.

      +
      Returns
      boolean

      Whether the message would be logged.

      +

      log(level, msg, opt_exception)code »

      Logs a message. If the logger is currently enabled for the +given message level then the given message is forwarded to all the +registered output Handler objects.

      +
      Parameters
      levelwebdriver.logging.Level

      One of the level identifiers.

      +
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Object=

      An exception associated with the +message.

      +

      logRecord(logRecord)code »

      Logs a LogRecord. If the logger is currently enabled for the +given message level then the given message is forwarded to all the +registered output Handler objects.

      +
      Parameters
      logRecordwebdriver.logging.LogRecord

      A log record to log.

      +

      removeHandler(handler)code »

      Removes a handler from the logger. This doesn't use the event system because +we want to be able to add logging to the event system.

      +
      Parameters
      handlerFunction

      Handler function to remove.

      +
      Returns
      boolean

      Whether the handler was removed.

      +

      setLevel(level)code »

      Set the log level specifying which message levels will be logged by this +logger. Message levels lower than this value will be discarded. +The level value Level.OFF can be used to turn off logging. If the new level +is null, it means that this node should inherit its level from its nearest +ancestor with a specific (non-null) level value.

      +
      Parameters
      levelwebdriver.logging.Level

      The new level.

      +

      severe(msg, opt_exception)code »

      Logs a message at the Logger.Level.SEVERE level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      shout(msg, opt_exception)code »

      Logs a message at the Logger.Level.SHOUT level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      warning(msg, opt_exception)code »

      Logs a message at the Logger.Level.WARNING level. +If the logger is currently enabled for the given message level then the +given message is forwarded to all the registered output Handler objects.

      +
      Parameters
      msg(string|function(): string)

      The message to log.

      +
      opt_exception?Error=

      An exception associated with the message.

      +

      Static Functions

      Logger.getLogger(name)code »

      deprecated

      Finds or creates a logger for a named subsystem. If a logger has already been +created with the given name it is returned. Otherwise a new logger is +created. If a new logger is created its log level will be configured based +on the LogManager configuration and it will configured to also send logging +output to its parent's handlers. It will be registered in the LogManager +global namespace.

      +
      Deprecated

      use goog.log instead. http://go/goog-debug-logger-deprecated

      +
      Parameters
      namestring

      A name for the logger. This should be a dot-separated +name and should normally be based on the package name or class name of the +subsystem, such as goog.net.BrowserChannel.

      +
      Returns
      webdriver.logging.Logger

      The named logger.

      +

      Logger.logToProfilers(msg)code »

      Logs a message to profiling tools, if available. +https://developers.google.com/web-toolkit/speedtracer/logging-api +http://msdn.microsoft.com/en-us/library/dd433074(VS.85).aspx

      +
      Parameters
      msgstring

      The message to log.

      +

      Static Properties

      Compiler Constants

      Logger.ENABLE_HIERARCHYboolean

      Toggles whether loggers other than the root logger can have +log handlers attached to them and whether they can have their log level +set. Logging is a bit faster when this is set to false.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_logging_Preferences.html b/docs/class_webdriver_logging_Preferences.html new file mode 100644 index 0000000..baccf72 --- /dev/null +++ b/docs/class_webdriver_logging_Preferences.html @@ -0,0 +1,8 @@ +Preferences

      class Preferences

      Describes the log preferences for a WebDriver session.

      +

      new Preferences()

      Parameters
      None.

      Instance Methods

      setLevel(type, level)code »

      Sets the desired logging level for a particular log type.

      +
      Parameters
      typestring

      The log type.

      +
      levelwebdriver.logging.Level

      The desired log level.

      +

      toJSON(arg0)code »

      Converts this instance to its JSON representation.

      +
      Parameters
      arg0string=
      Returns
      Object<string, string>

      The JSON representation of this set of +preferences.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_promise_CanceledTaskError_.html b/docs/class_webdriver_promise_CanceledTaskError_.html index cd0697e..95c978b 100644 --- a/docs/class_webdriver_promise_CanceledTaskError_.html +++ b/docs/class_webdriver_promise_CanceledTaskError_.html @@ -1,4 +1,4 @@ -webdriver.promise.CanceledTaskError_

      Class webdriver.promise.CanceledTaskError_

      code »
      Error
      +webdriver.promise.CanceledTaskError_

      Class webdriver.promise.CanceledTaskError_

      code »
      Errorgoog.debug.Error
             └ webdriver.promise.CanceledTaskError_

      Special error used to signal when a task is canceled because a previous - task in the same frame failed.

      Constructor

      webdriver.promise.CanceledTaskError_ ( err )
      Parameters
      err: *
      The error that caused the task cancellation.
      Show:

      Static Properties

      \ No newline at end of file + task in the same frame failed.

      Constructor

      webdriver.promise.CanceledTaskError_ ( err )
      Parameters
      err: *
      The error that caused the task cancellation.
      Show:

      Static Properties

      \ No newline at end of file diff --git a/docs/class_webdriver_promise_CancellationError.html b/docs/class_webdriver_promise_CancellationError.html new file mode 100644 index 0000000..d1bc202 --- /dev/null +++ b/docs/class_webdriver_promise_CancellationError.html @@ -0,0 +1,17 @@ +CancellationError

      class CancellationError

      Error
      +  └ goog.debug.Error
      +      └ webdriver.promise.CancellationError

      Error used when the computation of a promise is cancelled.

      +

      new CancellationError(opt_msg)

      Parameters
      opt_msgstring=

      The cancellation message.

      +

      Instance Properties

      descriptionstring

      IE-only.

      +
      fileNamestring

      Mozilla-only

      +
      lineNumbernumber

      Mozilla-only.

      +
      messagestring
      No description.
      namestring
      No description.
      reportErrorToServerboolean

      Whether to report this error to the server. Setting this to false will +cause the error reporter to not report the error back to the server, +which can be useful if the client knows that the error has already been +logged on the server.

      +
      sourceURL?

      Doesn't seem to exist, but closure/debug.js references it.

      +
      stackstring
      No description.

      Static Functions

      CancellationError.wrap(error, opt_msg)code »

      Wraps the given error in a CancellationError.

      +
      Parameters
      error*

      The error to wrap.

      +
      opt_msgstring=

      The prefix message to use.

      +
      Returns
      webdriver.promise.CancellationError

      A cancellation error.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_promise_ControlFlow.html b/docs/class_webdriver_promise_ControlFlow.html index 1edb7cd..08ff2e7 100644 --- a/docs/class_webdriver_promise_ControlFlow.html +++ b/docs/class_webdriver_promise_ControlFlow.html @@ -1,116 +1,153 @@ -webdriver.promise.ControlFlow

      Class webdriver.promise.ControlFlow

      code »
      webdriver.EventEmitter
      -  └ webdriver.promise.ControlFlow

      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 - webdriver.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. - -

      Tasks and each callback registered on a 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. - -

      Each time a ControlFlow empties its task queue, it will fire an - 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

      webdriver.promise.ControlFlow ( opt_timer )
      Parameters
      opt_timer: webdriver.promise.ControlFlow.Timer=
      The timer object - to use. Should only be set for testing.

      Enumerations

      Show:

      Type Definitions

      code »webdriver.promise.ControlFlow.Timer : {clearInterval: function(number), clearTimeout: function(number), setInterval: function(!Function, number): number, setTimeout: function(!Function, number): number}
      No description.

      Instance Methods

      Defined in webdriver.promise.ControlFlow

      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.

      Parameters
      error: *
      The reason the frame is being aborted; typically either - an Error or string.

      Aborts this flow, abandoning all remaining tasks. If there are - listeners registered, an UNCAUGHT_EXCEPTION will be emitted with the - offending error, otherwise, the error will be rethrown to the - global error handler.

      Parameters
      error: *
      Object describing the error that caused the flow to - abort; usually either an Error or string value.
      code »annotateError ( e )!(Error|goog.testing.JsUnitException)

      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.

      Parameters
      e: !(Error|goog.testing.JsUnitException)
      The error to annotate.
      Returns
      The annotated error.

      Schedules a task that will wait for another promise to resolve. The resolved - promise's value will be returned as the task result.

      Parameters
      promise: !webdriver.promise.Promise
      The promise to wait on.
      Returns
      A promise that will resolve when the - task has completed.

      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 - 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.

      code »execute ( fn, opt_description )!webdriver.promise.Promise

      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.

      Parameters
      fn: !Function
      The function to call to start the task. If the - function returns a webdriver.promise.Promise, this instance - will wait for it to be resolved before starting the next task.
      opt_description: string=
      A description of the task.
      Returns
      A promise that will be resolved with - the result of the action.

      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.

      Returns
      A summary of this instance's recent task - activity.
      Returns
      The next task to execute, or - null if a frame was resolved.
      Returns
      The scheduled tasks still pending with this instance.

      Resets this instance, clearing its queue and removing all event listeners.

      Parameters
      frame: !webdriver.promise.Frame_
      The frame to resolve.

      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.

      code »runInNewFrame_ ( fn, callback, errback, opt_activate )

      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.

      Parameters
      fn: !Function
      The function to execute.
      callback: function(*)
      The function to call with a successful result.
      errback: function(*)
      The function to call if there is an error.
      opt_activate: boolean=
      Whether the active frame should be updated to - the newly created frame so tasks are treated as sub-tasks.

      Schedules the interval for this instance's event loop, if necessary.

      code »timeout ( ms, opt_description )!webdriver.promise.Promise

      Inserts a setTimeout into the command queue. This is equivalent to - a thread sleep in a synchronous programming language.

      Parameters
      ms: number
      The timeout delay, in milliseconds.
      opt_description: string=
      A description to accompany the timeout.
      Returns
      A promise that will be resolved with - the result of the action.

      Removes a completed task from this instance's history record. If any - tasks remain from aborted frames, those will be removed as well.

      code »wait ( condition, timeout, opt_message )!webdriver.promise.Promise

      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.

      Parameters
      condition: !Function
      The condition function to poll.
      timeout: number
      How long to wait, in milliseconds, for the condition - to hold before timing out.
      opt_message: string=
      An optional error message to include if the - wait times out; defaults to the empty string.
      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

      code »addListener ( type, listenerFn, opt_scope )!webdriver.EventEmitter

      Registers a listener.

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      Returns
      A self reference.
      code »addListener_ ( type, listenerFn, opt_scope, opt_oneshot )!webdriver.EventEmitter

      Registers a listener.

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      opt_oneshot: boolean=
      Whether the listener should be removed after - the first event is fired.
      Returns
      A self reference.
      code »emit ( type, var_args )

      Fires an event and calls all listeners.

      Parameters
      type: string
      The type of event to emit.
      var_args: ...*
      Any arguments to pass to each listener.
      code »listeners ( type )!Array

      Returns a mutable list of listeners for a specific type of event.

      Parameters
      type: string
      The type of event to retrieve the listeners for.
      Returns
      The registered listeners for - the given event type.
      code »on ( type, listenerFn, opt_scope )!webdriver.EventEmitter

      An alias for #addListener().

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      Returns
      A self reference.
      code »once ( type, listenerFn, opt_scope )!webdriver.EventEmitter

      Registers a one-time listener which will be called only the first time an - event is emitted, after which it will be removed.

      Parameters
      type: string
      The type of event to listen for.
      listenerFn: !Function
      The function to invoke when the event is fired.
      opt_scope: Object=
      The object in whose scope to invoke the listener.
      Returns
      A self reference.

      Removes all listeners for a specific type of event. If no event is - specified, all listeners across all types will be removed.

      Parameters
      opt_type: string=
      The type of event to remove listeners from.
      Returns
      A self reference.

      Removes a previously registered event listener.

      Parameters
      type: string
      The type of event to unregister.
      listenerFn: !Function
      The handler function to remove.
      Returns
      A self reference.

      Instance Properties

      Defined in webdriver.promise.ControlFlow

      Tracks the active execution frame for this instance. Lazily initialized - when the first task is scheduled.

      Interval ID for this instance's event loop.

      A list of recent tasks. Each time a new task is started, or a frame is - completed, the previously recorded task is removed from this list. If - there are multiple tasks, task N+1 is considered a sub-task of task - N.

      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 - null, 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.

      Timeout ID set when the flow is about to shutdown without any errors - being detected. Upon shutting down, the flow will emit an - 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);

      The timer used by this instance.

      Defined in webdriver.EventEmitter

      Map of events to registered listeners.

      Static Properties

      How often, in milliseconds, the event loop should run.

      The default timer object, which uses the global timer functions.

      \ No newline at end of file +ControlFlow

      class ControlFlow

      webdriver.EventEmitter
      +  └ webdriver.promise.ControlFlow

      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 +webdriver.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.

      +

      Tasks and each callback registered on a webdriver.promise.Promise +will be run in their own ControlFlow frame. Any tasks scheduled within a +frame will take priority over previously scheduled tasks. Furthermore, if any +of the tasks in the frame fail, 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.

      +

      Each time a ControlFlow empties its task queue, it will fire an +IDLE event. Conversely, +whenever the flow terminates due to an unhandled error, it will remove all +remaining tasks in its queue and fire an +UNCAUGHT_EXCEPTION event. If there are no listeners registered with the +flow, the error will be rethrown to the global error handler.

      +

      Refer to the webdriver.promise module documentation for a detailed +explanation of how the ControlFlow coordinates task execution.

      +

      new ControlFlow()

      Parameters
      None.

      Instance Methods

      addListener(type, listenerFn, opt_scope)code »

      Registers a listener.

      +

      Defined by: webdriver.EventEmitter

      Parameters
      typestring

      The type of event to listen for.

      +
      listenerFnFunction

      The function to invoke when the event is fired.

      +
      opt_scope?Object=

      The object in whose scope to invoke the listener.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      async(fn, opt_self, var_args)code »

      Executes a function in the next available turn of the JavaScript event +loop. This ensures the function runs with its own task queue and any +scheduled tasks will run in "parallel" to those scheduled in the current +function.

      +
      flow.execute(() => console.log('a'));
      +flow.execute(() => console.log('b'));
      +flow.execute(() => console.log('c'));
      +flow.async(() => {
      +   flow.execute(() => console.log('d'));
      +   flow.execute(() => console.log('e'));
      +});
      +flow.async(() => {
      +   flow.execute(() => console.log('f'));
      +   flow.execute(() => console.log('g'));
      +});
      +flow.once('idle', () => console.log('fin'));
      +// a
      +// d
      +// f
      +// b
      +// e
      +// g
      +// c
      +// fin
      +
      +

      If the function itself throws, the error will be treated the same as an +unhandled rejection within the control flow.

      +

      NOTE: This function is considered unstable.

      +
      Parameters
      fnFunction

      The function to execute.

      +
      opt_self?Object=

      The object in whose context to run the function.

      +
      var_args...*

      Any arguments to pass to the function.

      +

      emit(type, var_args)code »

      Fires an event and calls all listeners.

      +

      Defined by: webdriver.EventEmitter

      Parameters
      typestring

      The type of event to emit.

      +
      var_args...*

      Any arguments to pass to each listener.

      +

      <T> execute(fn, opt_description)code »

      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. If +the task function is a generator, the task will be executed using +webdriver.promise.consume.

      +
      Parameters
      fnfunction(): (T|webdriver.promise.Promise<T>)

      The function to +call to start the task. If the function returns a +webdriver.promise.Promise, this instance will wait for it to be +resolved before starting the next task.

      +
      opt_descriptionstring=

      A description of the task.

      +
      Returns
      webdriver.promise.Promise<T>

      A promise that will be resolved +with the result of the action.

      +

      getSchedule(opt_includeStackTraces)code »

      Generates an annotated string describing the internal state of this control +flow, including the currently executing as well as pending tasks. If +opt_includeStackTraces === true, the string will include the +stack trace from when each task was scheduled.

      +
      Parameters
      opt_includeStackTracesstring=

      Whether to include the stack traces +from when each task was scheduled. Defaults to false.

      +
      Returns
      string

      String representation of this flow's internal state.

      +

      isIdle()code »

      Returns
      boolean

      Whether this flow is currently idle.

      +

      listeners(type)code »

      Returns a mutable list of listeners for a specific type of event.

      +

      Defined by: webdriver.EventEmitter

      Parameters
      typestring

      The type of event to retrieve the listeners for.

      +
      Returns
      Array<{fn: Function, oneshot: boolean, scope: ?(Object)}>

      The registered listeners for +the given event type.

      +

      on(type, listenerFn, opt_scope)code »

      An alias for #addListener().

      +

      Defined by: webdriver.EventEmitter

      Parameters
      typestring

      The type of event to listen for.

      +
      listenerFnFunction

      The function to invoke when the event is fired.

      +
      opt_scope?Object=

      The object in whose scope to invoke the listener.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      once(type, listenerFn, opt_scope)code »

      Registers a one-time listener which will be called only the first time an +event is emitted, after which it will be removed.

      +

      Defined by: webdriver.EventEmitter

      Parameters
      typestring

      The type of event to listen for.

      +
      listenerFnFunction

      The function to invoke when the event is fired.

      +
      opt_scope?Object=

      The object in whose scope to invoke the listener.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      removeAllListeners(opt_type)code »

      Removes all listeners for a specific type of event. If no event is +specified, all listeners across all types will be removed.

      +

      Defined by: webdriver.EventEmitter

      Parameters
      opt_typestring=

      The type of event to remove listeners from.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      removeListener(type, listenerFn)code »

      Removes a previously registered event listener.

      +

      Defined by: webdriver.EventEmitter

      Parameters
      typestring

      The type of event to unregister.

      +
      listenerFnFunction

      The handler function to remove.

      +
      Returns
      webdriver.EventEmitter

      A self reference.

      +

      reset()code »

      Resets this instance, clearing its queue and removing all event listeners.

      +

      setPropagateUnhandledRejections(propagate)code »

      Sets whether any unhandled rejections should propagate up through the +control flow stack and cause rejections within parent tasks. If error +propagation is disabled, tasks will not be aborted when an unhandled +promise rejection is detected, but the rejection will trigger an +webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION +event.

      +

      The default behavior is to propagate all unhandled rejections. The use +of this option is highly discouraged.

      +
      Parameters
      propagateboolean

      whether to propagate errors.

      +

      timeout(ms, opt_description)code »

      Inserts a setTimeout into the command queue. This is equivalent to +a thread sleep in a synchronous programming language.

      +
      Parameters
      msnumber

      The timeout delay, in milliseconds.

      +
      opt_descriptionstring=

      A description to accompany the timeout.

      +
      Returns
      webdriver.promise.Promise

      A promise that will be resolved with +the result of the action.

      +

      toString()code »

      Returns a string representation of this control flow, which is its current +schedule, sans task stack traces.

      +
      Returns
      string

      The string representation of this contorl flow.

      +

      <T> wait(condition, opt_timeout, opt_message)code »

      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.

      +

      If the condition is defined as a promise, the flow will wait for it to +settle. If the timeout expires before the promise settles, the promise +returned by this function will be rejected.

      +

      If this function is invoked with timeout === 0, or the timeout is +omitted, the flow will wait indefinitely for the condition to be satisfied.

      +
      Parameters
      condition(webdriver.promise.Promise<T>|function(): ?)

      The condition to poll, +or a promise to wait on.

      +
      opt_timeoutnumber=

      How long to wait, in milliseconds, for the +condition to hold before timing out. If omitted, the flow will wait +indefinitely.

      +
      opt_messagestring=

      An optional error message to include if the +wait times out; defaults to the empty string.

      +
      Returns
      webdriver.promise.Promise<T>

      A promise that will be fulfilled +when the condition has been satisified. The promise shall be rejected +if the wait times out waiting for the condition.

      +
      Throws
      TypeError

      If condition is not a function or promise or if timeout +is not a number >= 0.

      +

      Types

      ControlFlow.EventType

      Events that may be emitted by an webdriver.promise.ControlFlow.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_promise_Deferred.html b/docs/class_webdriver_promise_Deferred.html index d98258e..3645e54 100644 --- a/docs/class_webdriver_promise_Deferred.html +++ b/docs/class_webdriver_promise_Deferred.html @@ -1,110 +1,84 @@ -webdriver.promise.Deferred

      Class webdriver.promise.Deferred

      code »
      webdriver.promise.Promise
      -  └ webdriver.promise.Deferred

      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 promise property that may be returned to consumers for - registering callbacks, reserving the ability to resolve the deferred to the - producer. +Deferred

      class Deferred<T>

      All implemented interfaces
      IThenable<T>
      webdriver.promise.Thenable<T>

      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 promise property that may be returned to consumers for +registering callbacks, reserving the ability to resolve the deferred to the +producer.

      +

      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 +webdriver.promise.ControlFlow as an unhandled failure.

      +

      new Deferred(opt_flow)

      Parameters
      opt_flow?webdriver.promise.ControlFlow=

      The control flow this instance was +created under. This should only be provided during unit tests.

      +

      Instance Methods

      cancel(opt_reason)code »

      Cancels the computation of this promise's value, rejecting the promise in +the process. This method is a no-op if the promise has already been +resolved.

      +

      Specified by: webdriver.promise.Thenable

      Parameters
      opt_reason?(string|webdriver.promise.CancellationError)=

      The reason this +promise is being cancelled.

      +

      fulfill(opt_value)code »

      Resolves this deferred with the given value. It is safe to call this as a +normal function (with no bound "this").

      +
      Parameters
      opt_value?(T|{then: ?})=

      The fulfilled value.

      +

      isPending()code »

      Specified by: webdriver.promise.Thenable

      Returns
      boolean

      Whether this promise's value is still being computed.

      +

      reject(opt_reason)code »

      Rejects this promise with the given reason. It is safe to call this as a +normal function (with no bound "this").

      +
      Parameters
      opt_reason*=

      The rejection reason.

      +

      then(opt_callback, opt_errback)code »

      deprecated

      Registers listeners for when this instance is resolved.

      +

      Specified by: webdriver.promise.Thenable, IThenable

      Deprecated

      Use then from the promise property directly.

      +
      Parameters
      opt_callback?function(T): (R|IThenable<R>)=

      The +function to call if this promise is successfully resolved. The function +should expect a single argument: the promise's resolved value.

      +
      opt_errback?function(*): (R|IThenable<R>)=

      The function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenCatch(errback)code »

      deprecated

      Registers a listener for when this promise is rejected. This is synonymous +with the catch clause in a synchronous API:

      +
      // Synchronous API:
      +try {
      +  doSynchronousWork();
      +} catch (ex) {
      +  console.error(ex);
      +}
       
      - 

      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 - webdriver.promise.ControlFlow as an unhandled failure. +// Asynchronous promise API: +doAsynchronousWork().thenCatch(function(ex) { + console.error(ex); +}); +

      +

      Specified by: webdriver.promise.Thenable

      Deprecated

      Use thenCatch from the promise property directly.

      +
      Parameters
      errbackfunction(*): (R|IThenable<R>)

      The +function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenFinally(callback)code »

      deprecated

      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 finally clause in a synchronous API:

      +
      // Synchronous API:
      +try {
      +  doSynchronousWork();
      +} finally {
      +  cleanUp();
      +}
       
      - 

      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.

      Constructor

      webdriver.promise.Deferred ( opt_canceller, opt_flow )
      Parameters
      opt_canceller: Function=
      Function to call when cancelling the - computation of this instance's value.
      opt_flow: webdriver.promise.ControlFlow=
      The control flow - this instance was created under. This should only be provided during - unit tests.

      Enumerations

      Show:

      Type Definitions

      code »webdriver.promise.Deferred.Listener_ : {callback: (Function|undefined), errback: (Function|undefined), fulfill: function(*), reject: function(*)}
      Type definition for a listener registered on a Deferred object.

      Instance Methods

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will - be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.
      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a - promise and not a reference to this deferred, this instance will wait for - it before resolving.

      Parameters
      opt_value: *=
      The resolved value.
      code »reject ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will - be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise

      code »addBoth ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenFinally() instead.

      Registers a function to be invoked when this promise is either rejected or - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call when this promise is - either resolved or rejected. The function should expect a single - argument: the resolved value or rejection error.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallback ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      Registers a function to be invoked when this promise is successfully - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallbacks ( callback, errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      An alias for webdriver.promise.Promise.prototype.then that permits - the scope of the invoked function to be specified. This function is provided - for backwards compatibility with the Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addErrback ( errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenCatch() instead.

      Registers a function to be invoked when this promise is rejected. - This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »cancel ( reason )

      Cancels the computation of this promise's value, rejecting the promise in the - process.

      Parameters
      reason: *
      The reason this promise is being cancelled. If not an - Error, one will be created using the value's string - representation.
      Returns
      Whether this promise's value is still being computed.
      code »then ( opt_callback, opt_errback )!webdriver.promise.Promise

      Registers listeners for when this instance is resolved. This function most - overridden by subtypes.

      Parameters
      opt_callback: Function=
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_errback: Function=
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      Registers a listener for when this promise is rejected. This is synonymous - with the catch clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } catch (ex) {
      -     console.error(ex);
      -   }
      +// Asynchronous promise API:
      +doAsynchronousWork().thenFinally(cleanUp);
      +
      +

      Note: similar to the 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
      +}
       
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenCatch(function(ex) {
      -     console.error(ex);
      -   });
      - 
      Parameters
      errback: !Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      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 finally clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } finally {
      -     cleanUp();
      -   }
      -
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenFinally(cleanUp);
      - 
      - - Note: similar to the 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
      callback

      Instance Properties

      Defined in webdriver.promise.Deferred

      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. - -

      This class is based on the Promise/A proposal from CommonJS. Additional - functions are provided for API compatibility with Dojo Deferred objects.

      Static Properties

      \ No newline at end of file +promise.rejected(Error('one')) + .thenFinally(function() { + throw Error('two'); // Hides Error: one + }); + +

      Specified by: webdriver.promise.Thenable

      Deprecated

      Use thenFinally from the promise property directly.

      +
      Parameters
      callbackfunction(): (R|IThenable<R>)

      The function +to call when this promise is resolved.

      +
      Returns
      webdriver.promise.Promise

      A promise that will be fulfilled +with the callback result.

      +

      Instance Properties

      \ No newline at end of file diff --git a/docs/class_webdriver_promise_Frame_.html b/docs/class_webdriver_promise_Frame_.html index 798287f..19134a6 100644 --- a/docs/class_webdriver_promise_Frame_.html +++ b/docs/class_webdriver_promise_Frame_.html @@ -1,14 +1,14 @@ -webdriver.promise.Frame_

      Class webdriver.promise.Frame_

      code »
      webdriver.promise.Promise
      +webdriver.promise.Frame_

      Class webdriver.promise.Frame_

      code »
      webdriver.promise.Promise.<(T|null)>
         └ webdriver.promise.Deferredwebdriver.promise.Node_
      -          └ webdriver.promise.Frame_

      An execution frame within a webdriver.promise.ControlFlow. Each + └ webdriver.promise.Frame_

      All implemented interfaces:
      webdriver.promise.Thenable.<(T|null)>

      An execution frame within a webdriver.promise.ControlFlow. Each frame represents the execution context for either a webdriver.promise.Task_ or a callback on a webdriver.promise.Deferred.

      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.

      Constructor

      webdriver.promise.Frame_ ( flow )
      Parameters
      flow: !webdriver.promise.ControlFlow
      The flow this instance belongs - to.
      Show:

      Instance Methods

      Defined in webdriver.promise.Frame_

      Adds a new node to this frame.

      Parameters
      node: !(webdriver.promise.Frame_|webdriver.promise.Task_)
      The node to insert.

      Marks all of the tasks that are descendants of this frame in the execution + to.

      Show:

      Instance Methods

      Defined in webdriver.promise.Frame_

      Adds a new node to this frame.

      Parameters
      node: !(webdriver.promise.Frame_|webdriver.promise.Task_)
      The node to insert.

      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: @@ -27,95 +27,14 @@ // flow failed: Error: boom // task failed! CanceledTaskError: Task discarded due to a previous // task failure: Error: boom

      Parameters
      error: !webdriver.promise.CanceledTaskError_
      The cancellation - error.
      Returns
      This frame's - fist child.
      Returns
      The task currently executing - within this frame, if any.

      Locks this frame.

      Removes a child from this frame.

      Parameters
      child: !(webdriver.promise.Frame_|webdriver.promise.Task_)
      The child to remove.
      Parameters
      task: webdriver.promise.Task_
      The task currently - executing within this frame, if any.
      code »toString ( )string

      Defined in webdriver.promise.Node_

      Returns
      This node's parent.
      Returns
      The root of this node's tree.
      code »setParent ( parent )
      Parameters
      parent: webdriver.promise.Node_
      This node's new parent.

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will + error.

      Returns
      This frame's + fist child.
      Returns
      The task currently executing + within this frame, if any.

      Locks this frame.

      Removes a child from this frame.

      Parameters
      child: !(webdriver.promise.Frame_|webdriver.promise.Task_)
      The child to remove.
      Parameters
      task: webdriver.promise.Task_
      The task currently + executing within this frame, if any.
      code »toString ( )string

      Defined in webdriver.promise.Node_

      Returns
      This node's parent.
      Returns
      The root of this node's tree.
      code »setParent ( parent )
      Parameters
      parent: webdriver.promise.Node_
      This node's new parent.

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.
      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a + Error or a string.

      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a promise and not a reference to this deferred, this instance will wait for - it before resolving.

      Parameters
      opt_value: *=
      The resolved value.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise

      code »addBoth ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenFinally() instead.

      Registers a function to be invoked when this promise is either rejected or - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call when this promise is - either resolved or rejected. The function should expect a single - argument: the resolved value or rejection error.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallback ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      Registers a function to be invoked when this promise is successfully - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallbacks ( callback, errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      An alias for webdriver.promise.Promise.prototype.then that permits - the scope of the invoked function to be specified. This function is provided - for backwards compatibility with the Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addErrback ( errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenCatch() instead.

      Registers a function to be invoked when this promise is rejected. - This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »cancel ( reason )

      Cancels the computation of this promise's value, rejecting the promise in the - process.

      Parameters
      reason: *
      The reason this promise is being cancelled. If not an - Error, one will be created using the value's string - representation.
      Returns
      Whether this promise's value is still being computed.
      code »then ( opt_callback, opt_errback )!webdriver.promise.Promise

      Registers listeners for when this instance is resolved. This function most - overridden by subtypes.

      Parameters
      opt_callback: Function=
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_errback: Function=
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      Registers a listener for when this promise is rejected. This is synonymous - with the 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
      errback: !Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      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 finally clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } finally {
      -     cleanUp();
      -   }
      -
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenFinally(cleanUp);
      - 
      - - Note: similar to the 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
      callback

      Instance Properties

      Defined in webdriver.promise.Frame_

      Whether this frame is active. A frame is considered active once one of its + it before resolving.

      Parameters
      opt_value: T=
      The fulfilled value.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise.<(T|null)>

      code »cancel ( reason )
      Parameters
      reason
      code »isPending ( )boolean
      code »then ( opt_callback, opt_errback )
      Parameters
      opt_callback
      opt_errback
      code »thenCatch ( errback )
      Parameters
      errback
      code »thenFinally ( callback )
      Parameters
      callback

      Instance Properties

      Defined in webdriver.promise.Frame_

      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 @@ -128,15 +47,13 @@ flow.execute('this should execute 2nd', goog.nullFunction); }); flow.execute('this should execute last', goog.nullFunction); -

      Whether this frame is currently locked. A locked frame represents a callback +

      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 active, any new frames which are added represent callbacks on a webdriver.promise.Deferred, whose - tasks must be given priority over previously scheduled tasks.

      A reference to the last node inserted in this frame.

      The task currently being executed within this frame.

      Defined in webdriver.promise.Node_

      Defined in webdriver.promise.Deferred

      Represents the eventual value of a completed operation. Each promise may be + tasks must be given priority over previously scheduled tasks.

      A reference to the last node inserted in this frame.

      The task currently being executed within this frame.

      Defined in webdriver.promise.Node_

      Defined in webdriver.promise.Deferred

      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. - -

      This class is based on the Promise/A proposal from CommonJS. Additional - functions are provided for API compatibility with Dojo Deferred objects.

      Static Properties

      \ No newline at end of file + fulfilled or rejected state, at which point the promise is considered + resolved.

      Static Properties

      \ No newline at end of file diff --git a/docs/class_webdriver_promise_MultipleUnhandledRejectionError.html b/docs/class_webdriver_promise_MultipleUnhandledRejectionError.html new file mode 100644 index 0000000..5f52808 --- /dev/null +++ b/docs/class_webdriver_promise_MultipleUnhandledRejectionError.html @@ -0,0 +1,14 @@ +MultipleUnhandledRejectionError

      class MultipleUnhandledRejectionError

      Error
      +  └ goog.debug.Error
      +      └ webdriver.promise.MultipleUnhandledRejectionError

      Error used when there are multiple unhandled promise rejections detected +within a task or callback.

      +

      new MultipleUnhandledRejectionError(errors)

      Parameters
      errorsSet<*>

      The errors to report.

      +

      Instance Properties

      descriptionstring

      IE-only.

      +
      errorsSet<*>
      No description.
      fileNamestring

      Mozilla-only

      +
      lineNumbernumber

      Mozilla-only.

      +
      messagestring
      No description.
      namestring
      No description.
      reportErrorToServerboolean

      Whether to report this error to the server. Setting this to false will +cause the error reporter to not report the error back to the server, +which can be useful if the client knows that the error has already been +logged on the server.

      +
      sourceURL?

      Doesn't seem to exist, but closure/debug.js references it.

      +
      stackstring
      No description.
      \ No newline at end of file diff --git a/docs/class_webdriver_promise_Node_.html b/docs/class_webdriver_promise_Node_.html index f815628..666e39a 100644 --- a/docs/class_webdriver_promise_Node_.html +++ b/docs/class_webdriver_promise_Node_.html @@ -1,97 +1,14 @@ -webdriver.promise.Node_

      Class webdriver.promise.Node_

      code »
      webdriver.promise.Promise
      +webdriver.promise.Node_

      Class webdriver.promise.Node_

      code »
      webdriver.promise.Promise.<(T|null)>
         └ webdriver.promise.Deferred
      -      └ webdriver.promise.Node_

      A single node in an webdriver.promise.ControlFlow's task tree.

      Constructor

      webdriver.promise.Node_ ( flow )
      Parameters
      flow: !webdriver.promise.ControlFlow
      The flow this instance belongs - to.
      Show:

      Instance Methods

      Defined in webdriver.promise.Node_

      Returns
      This node's parent.
      Returns
      The root of this node's tree.
      code »setParent ( parent )
      Parameters
      parent: webdriver.promise.Node_
      This node's new parent.

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will + └ webdriver.promise.Node_

      All implemented interfaces:
      webdriver.promise.Thenable.<(T|null)>

      A single node in an webdriver.promise.ControlFlow's task tree.

      Constructor

      webdriver.promise.Node_ ( flow )
      Parameters
      flow: !webdriver.promise.ControlFlow
      The flow this instance belongs + to.
      Show:

      Instance Methods

      Defined in webdriver.promise.Node_

      Returns
      This node's parent.
      Returns
      The root of this node's tree.
      code »setParent ( parent )
      Parameters
      parent: webdriver.promise.Node_
      This node's new parent.

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.
      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a + Error or a string.

      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a promise and not a reference to this deferred, this instance will wait for - it before resolving.

      Parameters
      opt_value: *=
      The resolved value.
      code »reject ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will + it before resolving.

      Parameters
      opt_value: T=
      The fulfilled value.
      code »reject ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise

      code »addBoth ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenFinally() instead.

      Registers a function to be invoked when this promise is either rejected or - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call when this promise is - either resolved or rejected. The function should expect a single - argument: the resolved value or rejection error.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallback ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      Registers a function to be invoked when this promise is successfully - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallbacks ( callback, errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      An alias for webdriver.promise.Promise.prototype.then that permits - the scope of the invoked function to be specified. This function is provided - for backwards compatibility with the Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addErrback ( errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenCatch() instead.

      Registers a function to be invoked when this promise is rejected. - This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »cancel ( reason )

      Cancels the computation of this promise's value, rejecting the promise in the - process.

      Parameters
      reason: *
      The reason this promise is being cancelled. If not an - Error, one will be created using the value's string - representation.
      Returns
      Whether this promise's value is still being computed.
      code »then ( opt_callback, opt_errback )!webdriver.promise.Promise

      Registers listeners for when this instance is resolved. This function most - overridden by subtypes.

      Parameters
      opt_callback: Function=
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_errback: Function=
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      Registers a listener for when this promise is rejected. This is synonymous - with the 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
      errback: !Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      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 finally clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } finally {
      -     cleanUp();
      -   }
      -
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenFinally(cleanUp);
      - 
      - - Note: similar to the 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
      callback

      Instance Properties

      Defined in webdriver.promise.Node_

      Defined in webdriver.promise.Deferred

      Represents the eventual value of a completed operation. Each promise may be + Error or a string.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise.<(T|null)>

      code »cancel ( reason )
      Parameters
      reason
      code »isPending ( )boolean
      code »then ( opt_callback, opt_errback )
      Parameters
      opt_callback
      opt_errback
      code »thenCatch ( errback )
      Parameters
      errback
      code »thenFinally ( callback )
      Parameters
      callback

      Instance Properties

      Defined in webdriver.promise.Node_

      Defined in webdriver.promise.Deferred

      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. - -

      This class is based on the Promise/A proposal from CommonJS. Additional - functions are provided for API compatibility with Dojo Deferred objects.

      Static Properties

      \ No newline at end of file + fulfilled or rejected state, at which point the promise is considered + resolved.

      Static Properties

      \ No newline at end of file diff --git a/docs/class_webdriver_promise_Promise.html b/docs/class_webdriver_promise_Promise.html index 0bab2ee..a3ba277 100644 --- a/docs/class_webdriver_promise_Promise.html +++ b/docs/class_webdriver_promise_Promise.html @@ -1,88 +1,75 @@ -webdriver.promise.Promise

      Class webdriver.promise.Promise

      code »

      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. +Promise

      class Promise<T>

      All implemented interfaces
      IThenable<T>
      webdriver.promise.Thenable<T>

      Represents the eventual value of a completed operation. Each promise may be +in one of three states: pending, fulfilled, or rejected. Each promise starts +in the pending state and may make a single transition to either a +fulfilled or rejected state, at which point the promise is considered +resolved.

      +

      new Promise(resolver, opt_flow)

      Parameters
      resolverfunction(function(?(T|{then: ?})=): ?, function(*=): ?): ?

      Function that is invoked immediately to begin computation of this +promise's value. The function should accept a pair of callback +functions, one for fulfilling the promise and another for rejecting it.

      +
      opt_flow?webdriver.promise.ControlFlow=

      The control flow +this instance was created under. Defaults to the currently active flow.

      +

      Instance Methods

      cancel(opt_reason)code »

      Cancels the computation of this promise's value, rejecting the promise in +the process. This method is a no-op if the promise has already been +resolved.

      +

      Specified by: webdriver.promise.Thenable

      Parameters
      opt_reason?(string|webdriver.promise.CancellationError)=

      The reason this +promise is being cancelled.

      +

      isPending()code »

      Specified by: webdriver.promise.Thenable

      Returns
      boolean

      Whether this promise's value is still being computed.

      +

      then(opt_callback, opt_errback)code »

      Registers listeners for when this instance is resolved.

      +

      Specified by: webdriver.promise.Thenable, IThenable

      Parameters
      opt_callback?function(T): (R|IThenable<R>)=

      The +function to call if this promise is successfully resolved. The function +should expect a single argument: the promise's resolved value.

      +
      opt_errback?function(*): (R|IThenable<R>)=

      The function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenCatch(errback)code »

      Registers a listener for when this promise is rejected. This is synonymous +with the catch clause in a synchronous API:

      +
      // Synchronous API:
      +try {
      +  doSynchronousWork();
      +} catch (ex) {
      +  console.error(ex);
      +}
       
      - 

      This class is based on the Promise/A proposal from CommonJS. Additional - functions are provided for API compatibility with Dojo Deferred objects.

      Constructor

      webdriver.promise.Promise ( )
      Show:

      Instance Methods

      code »addBoth ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenFinally() instead.

      Registers a function to be invoked when this promise is either rejected or - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call when this promise is - either resolved or rejected. The function should expect a single - argument: the resolved value or rejection error.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallback ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      Registers a function to be invoked when this promise is successfully - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallbacks ( callback, errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      An alias for webdriver.promise.Promise.prototype.then that permits - the scope of the invoked function to be specified. This function is provided - for backwards compatibility with the Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addErrback ( errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenCatch() instead.

      Registers a function to be invoked when this promise is rejected. - This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »cancel ( reason )

      Cancels the computation of this promise's value, rejecting the promise in the - process.

      Parameters
      reason: *
      The reason this promise is being cancelled. If not an - Error, one will be created using the value's string - representation.
      Returns
      Whether this promise's value is still being computed.
      code »then ( opt_callback, opt_errback )!webdriver.promise.Promise

      Registers listeners for when this instance is resolved. This function most - overridden by subtypes.

      Parameters
      opt_callback: Function=
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_errback: Function=
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      Registers a listener for when this promise is rejected. This is synonymous - with the 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);
      +});
      +
      +

      Specified by: webdriver.promise.Thenable

      Parameters
      errbackfunction(*): (R|IThenable<R>)

      The +function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

      +
      Returns
      webdriver.promise.Promise

      A new promise which will be +resolved with the result of the invoked callback.

      +

      thenFinally(callback)code »

      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 finally clause in a synchronous API:

      +
      // Synchronous API:
      +try {
      +  doSynchronousWork();
      +} finally {
      +  cleanUp();
      +}
       
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenCatch(function(ex) {
      -     console.error(ex);
      -   });
      - 
      Parameters
      errback: !Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      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 finally clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } finally {
      -     cleanUp();
      -   }
      +// Asynchronous promise API:
      +doAsynchronousWork().thenFinally(cleanUp);
      +
      +

      Note: similar to the 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
      +}
       
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenFinally(cleanUp);
      - 
      - - Note: similar to the 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
      callback
      \ No newline at end of file +promise.rejected(Error('one')) + .thenFinally(function() { + throw Error('two'); // Hides Error: one + }); + +

      Specified by: webdriver.promise.Thenable

      Parameters
      callbackfunction(): (R|IThenable<R>)

      The function +to call when this promise is resolved.

      +
      Returns
      webdriver.promise.Promise

      A promise that will be fulfilled +with the callback result.

      +

      toString()code »

      Returns
      string
      \ No newline at end of file diff --git a/docs/class_webdriver_promise_Task_.html b/docs/class_webdriver_promise_Task_.html index bc36581..f226db6 100644 --- a/docs/class_webdriver_promise_Task_.html +++ b/docs/class_webdriver_promise_Task_.html @@ -1,101 +1,18 @@ -webdriver.promise.Task_

      Class webdriver.promise.Task_

      code »
      webdriver.promise.Promise
      +webdriver.promise.Task_

      Class webdriver.promise.Task_

      code »
      webdriver.promise.Promise.<(T|null)>
         └ webdriver.promise.Deferredwebdriver.promise.Node_
      -          └ webdriver.promise.Task_

      A task to be executed by a webdriver.promise.ControlFlow.

      Constructor

      webdriver.promise.Task_ ( flow, fn, description, snapshot )
      Parameters
      flow: !webdriver.promise.ControlFlow
      The flow this instances belongs + └ webdriver.promise.Task_
      All implemented interfaces:
      webdriver.promise.Thenable.<(T|null)>

      A task to be executed by a webdriver.promise.ControlFlow.

      Constructor

      webdriver.promise.Task_ ( flow, fn, description, snapshot )
      Parameters
      flow: !webdriver.promise.ControlFlow
      The flow this instances belongs to.
      fn: !Function
      The function to call when the task executes. If it returns a webdriver.promise.Promise, the flow will wait for it to be resolved before starting the next task.
      description: string
      A description of the task for debugging.
      snapshot: !webdriver.stacktrace.Snapshot
      A snapshot of the stack - when this task was scheduled.
      Show:

      Instance Methods

      Defined in webdriver.promise.Task_

      Executes this task.

      Returns
      This task's description.
      code »toString ( )string

      Defined in webdriver.promise.Node_

      Returns
      This node's parent.
      Returns
      The root of this node's tree.
      code »setParent ( parent )
      Parameters
      parent: webdriver.promise.Node_
      This node's new parent.

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will + when this task was scheduled.

      Show:

      Instance Methods

      Defined in webdriver.promise.Task_

      Executes this task.

      Returns
      This task's description.
      code »toString ( )string

      Defined in webdriver.promise.Node_

      Returns
      This node's parent.
      Returns
      The root of this node's tree.
      code »setParent ( parent )
      Parameters
      parent: webdriver.promise.Node_
      This node's new parent.

      Defined in webdriver.promise.Deferred

      code »errback ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.
      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a + Error or a string.

      code »fulfill ( opt_value )

      Resolves this promise with the given value. If the value is itself a promise and not a reference to this deferred, this instance will wait for - it before resolving.

      Parameters
      opt_value: *=
      The resolved value.
      code »reject ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will + it before resolving.

      Parameters
      opt_value: T=
      The fulfilled value.
      code »reject ( opt_error )

      Rejects this promise. If the error is itself a promise, this instance will be chained to it and be rejected with the error's resolved value.

      Parameters
      opt_error: *=
      The rejection reason, typically either a - Error or a string.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise

      code »addBoth ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenFinally() instead.

      Registers a function to be invoked when this promise is either rejected or - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call when this promise is - either resolved or rejected. The function should expect a single - argument: the resolved value or rejection error.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallback ( callback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      Registers a function to be invoked when this promise is successfully - resolved. This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addCallbacks ( callback, errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #then() instead.

      An alias for webdriver.promise.Promise.prototype.then that permits - the scope of the invoked function to be specified. This function is provided - for backwards compatibility with the Dojo Deferred API.

      Parameters
      callback: Function
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »addErrback ( errback, opt_self )!webdriver.promise.Promise
      Deprecated: Use #thenCatch() instead.

      Registers a function to be invoked when this promise is rejected. - This function is provided for backwards compatibility with the - Dojo Deferred API.

      Parameters
      errback: Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      opt_self: !Object=
      The object which |this| should refer to when the - function is invoked.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.
      code »cancel ( reason )

      Cancels the computation of this promise's value, rejecting the promise in the - process.

      Parameters
      reason: *
      The reason this promise is being cancelled. If not an - Error, one will be created using the value's string - representation.
      Returns
      Whether this promise's value is still being computed.
      code »then ( opt_callback, opt_errback )!webdriver.promise.Promise

      Registers listeners for when this instance is resolved. This function most - overridden by subtypes.

      Parameters
      opt_callback: Function=
      The function to call if this promise is - successfully resolved. The function should expect a single argument: the - promise's resolved value.
      opt_errback: Function=
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      Registers a listener for when this promise is rejected. This is synonymous - with the 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
      errback: !Function
      The function to call if this promise is - rejected. The function should expect a single argument: the rejection - reason.
      Returns
      A new promise which will be resolved - with the result of the invoked callback.

      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 finally clause in a synchronous API: -

      
      -   // Synchronous API:
      -   try {
      -     doSynchronousWork();
      -   } finally {
      -     cleanUp();
      -   }
      -
      -   // Asynchronous promise API:
      -   doAsynchronousWork().thenFinally(cleanUp);
      - 
      - - Note: similar to the 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
      callback

      Instance Properties

      Defined in webdriver.promise.Task_

      Defined in webdriver.promise.Node_

      Defined in webdriver.promise.Deferred

      Represents the eventual value of a completed operation. Each promise may be + Error or a string.

      Removes all of the listeners previously registered on this deferred.

      Throws
      Error
      If this deferred has already been resolved.

      Defined in webdriver.promise.Promise.<(T|null)>

      code »cancel ( reason )
      Parameters
      reason
      code »isPending ( )boolean
      code »then ( opt_callback, opt_errback )
      Parameters
      opt_callback
      opt_errback
      code »thenCatch ( errback )
      Parameters
      errback
      code »thenFinally ( callback )
      Parameters
      callback

      Instance Properties

      Defined in webdriver.promise.Task_

      Defined in webdriver.promise.Node_

      Defined in webdriver.promise.Deferred

      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. - -

      This class is based on the Promise/A proposal from CommonJS. Additional - functions are provided for API compatibility with Dojo Deferred objects.

      Static Properties

      \ No newline at end of file + fulfilled or rejected state, at which point the promise is considered + resolved.

      Static Properties

      \ No newline at end of file diff --git a/docs/class_webdriver_stacktrace_Frame.html b/docs/class_webdriver_stacktrace_Frame.html index 68edecc..4b77d11 100644 --- a/docs/class_webdriver_stacktrace_Frame.html +++ b/docs/class_webdriver_stacktrace_Frame.html @@ -1,8 +1,20 @@ -webdriver.stacktrace.Frame

      Class webdriver.stacktrace.Frame

      code »

      Class representing one stack frame.

      Constructor

      webdriver.stacktrace.Frame ( context, name, alias, path )
      Parameters
      context: (string|undefined)
      Context object, empty in case of global - functions or if the browser doesn't provide this information.
      name: (string|undefined)
      Function name, empty in case of anonymous - functions.
      alias: (string|undefined)
      Alias of the function if available. For - example the function name will be 'c' and the alias will be 'b' if the - function is defined as a.b = function c() {};.
      path: (string|undefined)
      File path or URL including line number and - optionally column number separated by colons.
      Show:

      Instance Methods

      Returns
      The column number if known and -1 if it is unknown.
      Returns
      The line number if known or -1 if it is unknown.
      Returns
      The function name or empty string if the function is - anonymous and the object field which it's assigned to is unknown.
      Returns
      The url or empty string if it is unknown.
      Returns
      Whether the stack frame contains an anonymous function.

      Converts this frame to its string representation using V8's stack trace - format: http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi

      Returns
      The string representation of this frame.

      Instance Properties

      \ No newline at end of file +Frame

      class Frame

      Class representing one stack frame.

      +

      new Frame(context, name, alias, path)

      Parameters
      context(string|undefined)

      Context object, empty in case of global +functions or if the browser doesn't provide this information.

      +
      name(string|undefined)

      Function name, empty in case of anonymous +functions.

      +
      alias(string|undefined)

      Alias of the function if available. For +example the function name will be 'c' and the alias will be 'b' if the +function is defined as a.b = function c() {};.

      +
      path(string|undefined)

      File path or URL including line number and +optionally column number separated by colons.

      +

      Instance Methods

      getColumn()code »

      Returns
      number

      The column number if known and -1 if it is unknown.

      +

      getLine()code »

      Returns
      number

      The line number if known or -1 if it is unknown.

      +

      getName()code »

      Returns
      string

      The function name or empty string if the function is +anonymous and the object field which it's assigned to is unknown.

      +

      getUrl()code »

      Returns
      string

      The url or empty string if it is unknown.

      +

      isAnonymous()code »

      Returns
      boolean

      Whether the stack frame contains an anonymous function.

      +

      toString()code »

      Converts this frame to its string representation using V8's stack trace +format: http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi

      +
      Returns
      string

      The string representation of this frame.

      +

      Instance Properties

      columnnumber
      No description.
      \ No newline at end of file diff --git a/docs/class_webdriver_stacktrace_Snapshot.html b/docs/class_webdriver_stacktrace_Snapshot.html index 0484ea1..b623cdf 100644 --- a/docs/class_webdriver_stacktrace_Snapshot.html +++ b/docs/class_webdriver_stacktrace_Snapshot.html @@ -1,5 +1,6 @@ -webdriver.stacktrace.Snapshot

      Class webdriver.stacktrace.Snapshot

      code »

      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.

      Constructor

      webdriver.stacktrace.Snapshot ( opt_slice )
      Parameters
      opt_slice: number=
      The number of frames to remove from the top of - the generated stack trace.
      Show:

      Instance Methods

      Returns
      The parsed stack trace.

      Instance Properties

      The parsed stack trace. This list is lazily generated the first time it is - accessed.

      The error's stacktrace. This must be accessed immediately to ensure Opera - computes the context correctly.

      \ No newline at end of file +Snapshot

      class Snapshot

      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.

      +

      new Snapshot(opt_slice)

      Parameters
      opt_slicenumber=

      The number of frames to remove from the top of +the generated stack trace.

      +

      Instance Methods

      getStacktrace()code »

      Returns
      Array<webdriver.stacktrace.Frame>

      The parsed stack trace.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_testing_Assertion.html b/docs/class_webdriver_testing_Assertion.html index 8aee79e..b19a1b1 100644 --- a/docs/class_webdriver_testing_Assertion.html +++ b/docs/class_webdriver_testing_Assertion.html @@ -1,32 +1,99 @@ -webdriver.testing.Assertion

      Class webdriver.testing.Assertion

      code »

      Utility for performing assertions against a given value. If the - value is a webdriver.promise.Promise, this assertion will wait - for it to resolve before applying any matchers.

      Constructor

      webdriver.testing.Assertion ( value )
      Parameters
      value: *
      The value to wrap and apply matchers to.

      Classes

      webdriver.testing.Assertion.DelegatingMatcher_
      Wraps an object literal implementing the Matcher interface.
      Show:

      Instance Methods

      code »apply ( matcher, opt_message )webdriver.promise.Promise

      Asserts that the given matcher 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
      matcher: !goog.labs.testing.Matcher
      The matcher to apply
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The deferred assertion result, or - null if the assertion was immediately applied.
      code »closeTo ( value, range, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a number within a given distance of an - expected value.

      Parameters
      value: number
      The expected value.
      range: number
      The maximum amount the actual value is permitted to - differ from the expected value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »contains ( value, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string or array-like structure - containing the given value.

      Parameters
      value: *
      The expected value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »endsWith ( suffix, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string ending with the given suffix.

      Parameters
      suffix: string
      The expected suffix.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »equalTo ( value, opt_message )webdriver.promise.Promise

      Asserts that the value managed by this assertion is strictly equal to the - given value.

      Parameters
      value: *
      The expected value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is a number strictly - greater than value.

      Parameters
      value: number
      The minimum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is a number >= the given - value.

      Parameters
      value: number
      The minimum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the wrapped value is an instance of the given class.

      Parameters
      ctor: !Function
      The expected class constructor.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is strictly false.

      Returns
      The assertion result.

      Asserts that the wrapped value is null.

      Parameters
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the wrapped value is null or undefined.

      Parameters
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is strictly true.

      Returns
      The assertion result.

      Asserts that the wrapped value is undefined.

      Parameters
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »lessThan ( value, opt_message )webdriver.promise.Promise

      Asserts that the value managed by this assertion is a number strictly less - than the given value.

      Parameters
      value: number
      The maximum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is a number <= the given - value.

      Parameters
      value: number
      The maximum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »matches ( regex, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string that matches the given RegExp.

      Parameters
      regex: !RegExp
      The regex to test.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »startsWith ( prefix, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string starting with the given prefix.

      Parameters
      prefix: string
      The expected prefix.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Instance Properties

      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);

      \ No newline at end of file +Assertion

      class Assertion

      Utility for performing assertions against a given value. If the +value is a webdriver.promise.Promise, this assertion will wait +for it to resolve before applying any matchers.

      +

      new Assertion(value)

      Parameters
      value*

      The value to wrap and apply matchers to.

      +

      Instance Methods

      apply(matcher, opt_message)code »

      Asserts that the given matcher 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
      matchergoog.labs.testing.Matcher

      The matcher to apply

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The deferred assertion result, or +null if the assertion was immediately applied.

      +

      closeTo(value, range, opt_message)code »

      Asserts that the wrapped value is a number within a given distance of an +expected value.

      +
      Parameters
      valuenumber

      The expected value.

      +
      rangenumber

      The maximum amount the actual value is permitted to +differ from the expected value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      contains(value, opt_message)code »

      Asserts that the wrapped value is a string or array-like structure +containing the given value.

      +
      Parameters
      value*

      The expected value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      endsWith(suffix, opt_message)code »

      Asserts that the wrapped value is a string ending with the given suffix.

      +
      Parameters
      suffixstring

      The expected suffix.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      equalTo(value, opt_message)code »

      Asserts that the value managed by this assertion is strictly equal to the +given value.

      +
      Parameters
      value*

      The expected value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      greaterThan(value, opt_message)code »

      Asserts that the value managed by this assertion is a number strictly +greater than value.

      +
      Parameters
      valuenumber

      The minimum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      greaterThanEqualTo(value, opt_message)code »

      Asserts that the value managed by this assertion is a number >= the given +value.

      +
      Parameters
      valuenumber

      The minimum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      instanceOf(ctor, opt_message)code »

      Asserts that the wrapped value is an instance of the given class.

      +
      Parameters
      ctorFunction

      The expected class constructor.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isFalse()code »

      Asserts that the value managed by this assertion is strictly false.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isNull(opt_message)code »

      Asserts that the wrapped value is null.

      +
      Parameters
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isNullOrUndefined(opt_message)code »

      Asserts that the wrapped value is null or undefined.

      +
      Parameters
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isTrue()code »

      Asserts that the value managed by this assertion is strictly true.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isUndefined(opt_message)code »

      Asserts that the wrapped value is undefined.

      +
      Parameters
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      lessThan(value, opt_message)code »

      Asserts that the value managed by this assertion is a number strictly less +than the given value.

      +
      Parameters
      valuenumber

      The maximum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      lessThanEqualTo(value, opt_message)code »

      Asserts that the value managed by this assertion is a number <= the given +value.

      +
      Parameters
      valuenumber

      The maximum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      matches(regex, opt_message)code »

      Asserts that the wrapped value is a string that matches the given RegExp.

      +
      Parameters
      regexRegExp

      The regex to test.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      startsWith(prefix, opt_message)code »

      Asserts that the wrapped value is a string starting with the given prefix.

      +
      Parameters
      prefixstring

      The expected prefix.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      Instance Properties

      iswebdriver.testing.Assertion

      A self reference provided for writing fluent assertions: +webdriver.testing.assert(x).is.equalTo(y);

      +
      notwebdriver.testing.NegatedAssertion

      Negates any matchers applied to this instance's value: +webdriver.testing.assert(x).not.equalTo(y);

      +

      Types

      Assertion.DelegatingMatcher_

      Wraps an object literal implementing the Matcher interface.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_testing_Assertion_DelegatingMatcher_.html b/docs/class_webdriver_testing_Assertion_DelegatingMatcher_.html index 22a3d65..1d6baea 100644 --- a/docs/class_webdriver_testing_Assertion_DelegatingMatcher_.html +++ b/docs/class_webdriver_testing_Assertion_DelegatingMatcher_.html @@ -1,3 +1,13 @@ -webdriver.testing.Assertion.DelegatingMatcher_

      Class webdriver.testing.Assertion.DelegatingMatcher_

      code »
      All implemented interfaces:
      goog.labs.testing.Matcher

      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.

      Constructor

      webdriver.testing.Assertion.DelegatingMatcher_ ( obj )
      Parameters
      obj: {matches: function(*): boolean, describe: function(): string}
      The object literal to delegate to.
      Show:

      Instance Methods

      code »matches ( value )
      Parameters
      value
      \ No newline at end of file +Assertion.DelegatingMatcher_

      class Assertion.DelegatingMatcher_

      All implemented interfaces
      goog.labs.testing.Matcher

      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.

      +

      new Assertion.DelegatingMatcher_(obj)

      Parameters
      obj{describe: function(): string, matches: function(*): boolean}

      The object literal to delegate to.

      +

      Instance Methods

      describe(value, opt_description)code »

      Describes why the matcher failed.

      +

      Specified by: goog.labs.testing.Matcher

      Parameters
      value*

      The value that didn't match.

      +
      opt_descriptionstring=

      A partial description to which the reason +will be appended.

      +
      Returns
      string

      Description of why the matcher failed.

      +

      matches(value)code »

      Determines whether a value matches the constraints of the match.

      +

      Specified by: goog.labs.testing.Matcher

      Parameters
      value*

      The object to match.

      +
      Returns
      boolean

      Whether the input value matches this matcher.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_testing_ContainsMatcher.html b/docs/class_webdriver_testing_ContainsMatcher.html index d56b8a3..898aed5 100644 --- a/docs/class_webdriver_testing_ContainsMatcher.html +++ b/docs/class_webdriver_testing_ContainsMatcher.html @@ -1 +1,11 @@ -webdriver.testing.ContainsMatcher

      Class webdriver.testing.ContainsMatcher

      code »
      All implemented interfaces:
      goog.labs.testing.Matcher

      Accepts strins or array-like structures that contain value.

      Constructor

      webdriver.testing.ContainsMatcher ( value )
      Parameters
      value: *
      The value to check for.
      Show:

      Instance Methods

      code »describe ( actualValue )string
      Parameters
      actualValue
      code »matches ( actualValue )boolean
      Parameters
      actualValue

      Instance Properties

      \ No newline at end of file +ContainsMatcher

      class ContainsMatcher

      All implemented interfaces
      goog.labs.testing.Matcher

      Accepts strins or array-like structures that contain value.

      +

      new ContainsMatcher(value)

      Parameters
      value*

      The value to check for.

      +

      Instance Methods

      describe(value, opt_description)code »

      Describes why the matcher failed.

      +

      Specified by: goog.labs.testing.Matcher

      Parameters
      value*

      The value that didn't match.

      +
      opt_descriptionstring=

      A partial description to which the reason +will be appended.

      +
      Returns
      string

      Description of why the matcher failed.

      +

      matches(value)code »

      Determines whether a value matches the constraints of the match.

      +

      Specified by: goog.labs.testing.Matcher

      Parameters
      value*

      The object to match.

      +
      Returns
      boolean

      Whether the input value matches this matcher.

      +
      \ No newline at end of file diff --git a/docs/class_webdriver_testing_NegatedAssertion.html b/docs/class_webdriver_testing_NegatedAssertion.html index 6a7d073..0bedb8f 100644 --- a/docs/class_webdriver_testing_NegatedAssertion.html +++ b/docs/class_webdriver_testing_NegatedAssertion.html @@ -1,26 +1,97 @@ -webdriver.testing.NegatedAssertion

      Class webdriver.testing.NegatedAssertion

      code »
      webdriver.testing.Assertion
      -  └ webdriver.testing.NegatedAssertion

      An assertion that negates any applied matchers.

      Constructor

      webdriver.testing.NegatedAssertion ( value )
      Parameters
      value: *
      The value to perform assertions on.
      Show:

      Instance Methods

      Defined in webdriver.testing.NegatedAssertion

      code »apply ( matcher, opt_message )(null|webdriver.promise.Promise)
      Parameters
      matcher
      opt_message

      Defined in webdriver.testing.Assertion

      code »closeTo ( value, range, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a number within a given distance of an - expected value.

      Parameters
      value: number
      The expected value.
      range: number
      The maximum amount the actual value is permitted to - differ from the expected value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »contains ( value, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string or array-like structure - containing the given value.

      Parameters
      value: *
      The expected value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »endsWith ( suffix, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string ending with the given suffix.

      Parameters
      suffix: string
      The expected suffix.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »equalTo ( value, opt_message )webdriver.promise.Promise

      Asserts that the value managed by this assertion is strictly equal to the - given value.

      Parameters
      value: *
      The expected value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is a number strictly - greater than value.

      Parameters
      value: number
      The minimum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is a number >= the given - value.

      Parameters
      value: number
      The minimum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the wrapped value is an instance of the given class.

      Parameters
      ctor: !Function
      The expected class constructor.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is strictly false.

      Returns
      The assertion result.

      Asserts that the wrapped value is null.

      Parameters
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the wrapped value is null or undefined.

      Parameters
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is strictly true.

      Returns
      The assertion result.

      Asserts that the wrapped value is undefined.

      Parameters
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »lessThan ( value, opt_message )webdriver.promise.Promise

      Asserts that the value managed by this assertion is a number strictly less - than the given value.

      Parameters
      value: number
      The maximum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Asserts that the value managed by this assertion is a number <= the given - value.

      Parameters
      value: number
      The maximum value.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »matches ( regex, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string that matches the given RegExp.

      Parameters
      regex: !RegExp
      The regex to test.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.
      code »startsWith ( prefix, opt_message )webdriver.promise.Promise

      Asserts that the wrapped value is a string starting with the given prefix.

      Parameters
      prefix: string
      The expected prefix.
      opt_message: string=
      A message to include if the matcher does not - accept the value wrapped by this assertion.
      Returns
      The assertion result.

      Instance Properties

      Defined in webdriver.testing.NegatedAssertion

      Defined in webdriver.testing.Assertion

      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);

      Static Properties

      \ No newline at end of file +NegatedAssertion

      class NegatedAssertion

      webdriver.testing.Assertion
      +  └ webdriver.testing.NegatedAssertion

      An assertion that negates any applied matchers.

      +

      new NegatedAssertion(value)

      Parameters
      value*

      The value to perform assertions on.

      +

      Instance Methods

      apply(matcher, opt_message)code »

      Asserts that the given matcher 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.

      +

      Overrides: webdriver.testing.Assertion

      Parameters
      matchergoog.labs.testing.Matcher

      The matcher to apply

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The deferred assertion result, or +null if the assertion was immediately applied.

      +

      closeTo(value, range, opt_message)code »

      Asserts that the wrapped value is a number within a given distance of an +expected value.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      valuenumber

      The expected value.

      +
      rangenumber

      The maximum amount the actual value is permitted to +differ from the expected value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      contains(value, opt_message)code »

      Asserts that the wrapped value is a string or array-like structure +containing the given value.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      value*

      The expected value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      endsWith(suffix, opt_message)code »

      Asserts that the wrapped value is a string ending with the given suffix.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      suffixstring

      The expected suffix.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      equalTo(value, opt_message)code »

      Asserts that the value managed by this assertion is strictly equal to the +given value.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      value*

      The expected value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      greaterThan(value, opt_message)code »

      Asserts that the value managed by this assertion is a number strictly +greater than value.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      valuenumber

      The minimum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      greaterThanEqualTo(value, opt_message)code »

      Asserts that the value managed by this assertion is a number >= the given +value.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      valuenumber

      The minimum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      instanceOf(ctor, opt_message)code »

      Asserts that the wrapped value is an instance of the given class.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      ctorFunction

      The expected class constructor.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isFalse()code »

      Asserts that the value managed by this assertion is strictly false.

      +

      Defined by: webdriver.testing.Assertion

      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isNull(opt_message)code »

      Asserts that the wrapped value is null.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isNullOrUndefined(opt_message)code »

      Asserts that the wrapped value is null or undefined.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isTrue()code »

      Asserts that the value managed by this assertion is strictly true.

      +

      Defined by: webdriver.testing.Assertion

      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      isUndefined(opt_message)code »

      Asserts that the wrapped value is undefined.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      lessThan(value, opt_message)code »

      Asserts that the value managed by this assertion is a number strictly less +than the given value.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      valuenumber

      The maximum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      lessThanEqualTo(value, opt_message)code »

      Asserts that the value managed by this assertion is a number <= the given +value.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      valuenumber

      The maximum value.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      matches(regex, opt_message)code »

      Asserts that the wrapped value is a string that matches the given RegExp.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      regexRegExp

      The regex to test.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      startsWith(prefix, opt_message)code »

      Asserts that the wrapped value is a string starting with the given prefix.

      +

      Defined by: webdriver.testing.Assertion

      Parameters
      prefixstring

      The expected prefix.

      +
      opt_messagestring=

      A message to include if the matcher does not +accept the value wrapped by this assertion.

      +
      Returns
      webdriver.promise.Promise

      The assertion result.

      +

      Instance Properties

      iswebdriver.testing.Assertion

      A self reference provided for writing fluent assertions: +webdriver.testing.assert(x).is.equalTo(y);

      +
      notwebdriver.testing.NegatedAssertion

      Negates any matchers applied to this instance's value: +webdriver.testing.assert(x).not.equalTo(y);

      +
      value*
      No description.
      \ No newline at end of file diff --git a/docs/class_webdriver_testing_TestCase.html b/docs/class_webdriver_testing_TestCase.html new file mode 100644 index 0000000..071792f --- /dev/null +++ b/docs/class_webdriver_testing_TestCase.html @@ -0,0 +1,76 @@ +webdriver.testing.TestCase

      Class webdriver.testing.TestCase

      code »
      goog.testing.TestCase
      +  └ webdriver.testing.TestCase

      Constructs a test case that synchronizes each test case with the singleton + webdriver.promise.ControlFlow.

      Constructor

      webdriver.testing.TestCase ( client, opt_name )
      Parameters
      client: !webdriver.testing.Client
      The test client to use for + reporting test results.
      opt_name: string=
      The name of the test case, defaults to + 'Untitled Test Case'.
      Show:

      Instance Methods

      Defined in webdriver.testing.TestCase

      Executes the next test inside its own webdriver.Application.

      code »logError ( name, opt_e )goog.testing.TestCase.Error
      Parameters
      name
      opt_e

      Executes a single test, scheduling each phase with the global application. + Each phase will wait for the application to go idle before moving on to the + next test phase. This function models the follow basic test flow: + + try { + this.setUp.call(test.scope); + test.ref.call(test.scope); + } catch (ex) { + onError(ex); + } finally { + try { + this.tearDown.call(test.scope); + } catch (e) { + onError(e); + } + }

      Parameters
      test: !goog.testing.TestCase.Test
      The test to run.
      onError: function(*)
      The function to call each time an error is + detected.
      Returns
      A promise that will be resolved when the + test has finished running.

      Defined in goog.testing.TestCase

      code »add ( test )

      Adds a new test to the test case.

      Parameters
      test: goog.testing.TestCase.Test
      The test to add.
      code »addNewTest ( name, ref, opt_scope )

      Creates and adds a new test. + + Convenience function to make syntax less awkward when not using automatic + test discovery.

      Parameters
      name: string
      The test name.
      ref: !Function
      Reference to the test function.
      opt_scope: !Object=
      Optional scope that the test function should be + called in.

      Adds any functions defined in the global scope that correspond to + lifecycle events for the test case. Overrides setUp, tearDown, setUpPage, + tearDownPage and runTests if they are defined.

      Adds any functions defined in the global scope that are prefixed with "test" + to the test case.

      Clears a timeout created by this.timeout().

      Parameters
      id: number
      A timeout id.

      Counts the number of files that were loaded for dependencies that are + required to run the test.

      Returns
      The number of files loaded.

      Creates a goog.testing.TestCase.Test from an auto-discovered + function.

      Parameters
      name: string
      The name of the function.
      ref: function(): void
      The auto-discovered function.
      Returns
      The newly created test.
      code »doError ( test, opt_e )

      Handles a test that failed.

      Parameters
      test: goog.testing.TestCase.Test
      The test that failed.
      opt_e: *=
      The exception object associated with the + failure or a string.

      Handles a test that passed.

      Parameters
      test: goog.testing.TestCase.Test
      The test that passed.

      Executes each of the tests.

      Finalizes the test case, called when the tests have finished executing.

      Returns the number of tests actually run in the test case, i.e. subtracting + any which are skipped.

      Returns
      The number of un-ignored tests.
      Returns
      The function name prefix used to auto-discover tests.
      Returns
      Time since the last batch of tests was started.

      Returns the number of tests contained in the test case.

      Returns
      The number of tests.
      code »getGlobals ( opt_prefix )!Array

      Gets list of objects that potentially contain test cases. For IE 8 and below, + this is the global "this" (for properties set directly on the global this or + window) and the RuntimeObject (for global variables and functions). For all + other browsers, the array simply contains the global this.

      Parameters
      opt_prefix: string=
      An optional prefix. If specified, only get things + under this prefix. Note that the prefix is only honored in IE, since it + supports the RuntimeObject: + http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx + TODO: Remove this option.
      Returns
      A list of objects that should be inspected.
      Returns
      The name of the test.

      Returns the number of script files that were loaded in order to run the test.

      Returns
      The number of script files.
      code »getReport ( opt_verbose )string

      Returns a string detailing the results from the test.

      Parameters
      opt_verbose: boolean=
      If true results will include data about all + tests, not just what failed.
      Returns
      The results from the test.

      Returns the amount of time it took for the test to run.

      Returns
      The run time, in milliseconds.

      Returns the test results object: a map from test names to a list of test + failures (if any exist).

      Returns
      Tests results object.

      Gets the tests.

      Returns
      The test array.

      Returns the current time.

      Returns
      HH:MM:SS.
      Returns
      Whether the test case is running inside the multi test + runner.
      Returns
      Whether the test was a success.
      code »log ( val )

      Logs an object to the console, if available.

      Parameters
      val: *
      The value to log. Will be ToString'd.

      Checks to see if the test should be marked as failed before it is run. + + If there was an error in setUpPage, we treat that as a failure for all tests + and mark them all as having failed.

      Parameters
      testCase: goog.testing.TestCase.Test
      The current test case.
      Returns
      Whether the test was marked as failed.

      Returns the current test and increments the pointer.

      Returns
      The current test case.
      Returns
      The current time in milliseconds, don't use goog.now as some + tests override it.

      Reorders the tests depending on the order field.

      Parameters
      tests: Array.<goog.testing.TestCase.Test>
      An array of tests to + reorder.
      code »pad_ ( number )string

      Pads a number to make it have a leading zero if it's less than 10.

      Parameters
      number: number
      The number to pad.
      Returns
      The resulting string.

      Resets the test case pointer, so that next returns the first test.

      Executes each of the tests. + Overridable by the individual test case. This allows test cases to defer + when the test is actually started. If overridden, finalize must be called + by the test to indicate it has finished.

      code »saveMessage ( message )

      Saves a message to the result set.

      Parameters
      message: string
      The message to save.
      code »setBatchTime ( batchTime )
      Parameters
      batchTime: number
      Time since the last batch of tests was started.

      Sets the callback function that should be executed when the tests have + completed.

      Parameters
      fn: Function
      The callback function.
      code »setTests ( tests )

      Sets the tests.

      Parameters
      tests: !Array.<goog.testing.TestCase.Test>
      A new test array.

      Gets called before every goog.testing.TestCase.Test is been executed. Can be + overridden to add set up functionality to each test.

      Gets called before any tests are executed. Can be overridden to set up the + environment for the whole test case.

      Can be overridden in test classes to indicate whether the tests in a case + should be run in that particular situation. For example, this could be used + to stop tests running in a particular browser, where browser support for + the class under test was absent.

      Returns
      Whether any of the tests in the case should be run.

      Gets called after every goog.testing.TestCase.Test has been executed. Can be + overriden to add tear down functionality to each test.

      Gets called after all tests have been executed. Can be overridden to tear + down the entire test case.

      code »timeout ( fn, time )number

      Calls a function after a delay, using the protected timeout.

      Parameters
      fn: Function
      The function to call.
      time: number
      Delay in milliseconds.
      Returns
      The timeout id.

      Trims a path to be only that after google3.

      Parameters
      path: string
      The path to trim.
      Returns
      The resulting string.

      Instance Properties

      Defined in webdriver.testing.TestCase

      code »client_ : !webdriver.testing.Client

      Defined in goog.testing.TestCase

      Time since the last batch of tests was started, if batchTime exceeds + #maxRunTime a timeout will be used to stop the tests blocking the + browser and a new batch will be started.

      Pointer to the current test.

      Exception object that was detected before a test runs.

      A name for the test case.

      Optional callback that will be executed when the test has finalized.

      The order to run the auto-discovered tests in.

      Object used to encapsulate the test results.

      Whether the test case is running.

      Timestamp for when the test was started.

      Whether the test case has ever tried to execute.

      Set of test names and/or indices to execute, or null if all tests should + be executed. + + Indices are included to allow automation tools to run a subset of the + tests without knowing the exact contents of the test file. + + Indices should only be used with SORTED ordering. + + Example valid values: +

        +
      • [testName] +
      • [testName1, testName2] +
      • [2] - will run the 3rd test in the order specified +
      • [1,3,5] +
      • [testName1, testName2, 3, 5] - will work +

        Array of test functions that can be executed.

        Static Properties

        \ No newline at end of file diff --git a/docs/class_webdriver_until_Condition.html b/docs/class_webdriver_until_Condition.html new file mode 100644 index 0000000..3f942fc --- /dev/null +++ b/docs/class_webdriver_until_Condition.html @@ -0,0 +1,7 @@ +Condition

        class Condition<OUT>

        finalstruct

        Defines a condition to

        +

        new Condition(message, fn)

        Parameters
        messagestring

        A descriptive error message. Should complete the +sentence "Waiting [...]"

        +
        fnfunction(webdriver.WebDriver): OUT

        The condition function to +evaluate on each iteration of the wait loop.

        +

        Instance Methods

        description()code »

        Returns
        string

        A description of this condition.

        +

        fn(arg0)code »

        Parameters
        arg0webdriver.WebDriver
        Returns
        OUT
        \ No newline at end of file diff --git a/docs/dossier.css b/docs/dossier.css index 53ce031..1a6a134 100644 --- a/docs/dossier.css +++ b/docs/dossier.css @@ -1 +1 @@ -article,details,main,nav,section,summary{display:block}html{font-size:10px}html,body{height:100%}body{margin:0;padding:0;font-size:1.2rem;font-size:1.2em;font-family:sans-serif;background:#fff;color:#424242}a{text-decoration:none}a[href]{color:#66f}a[href]:hover{text-decoration:underline}code{font-size:1.2rem}code.type,code.type a[href],code.type a.unresolved-link{font-weight:normal;color:#080}code.type a.unresolved-link{border-bottom:1px dotted #080}pre,p{margin:1rem 0}.args{color:#828282;font-weight:normal}h1{border-bottom:1px solid #8f8f8f;margin:.2rem 0 0 0;padding:.5rem 0 .1rem;font-size:2.5rem}h1 code{font-size:1.85rem}h1 .deprecation-notice{color:#828282;font-weight:normal;font-style:italic;font-size:.66em}table{border-collapse:collapse;border-spacing:0;margin:0;padding:0;width:100%}tr{border:0;margin:0;padding:0}th{border-bottom:1px solid #8f8f8f;text-align:left;padding-top:.5rem}td>dl{margin-left:1rem}summary:focus{outline:0}summary::-webkit-details-marker{display:none}a.source{float:right}header>dl{margin-left:0}header>dl,header>pre,header>a.source{margin-top:.5rem}header+*{clear:right}dl{margin:0 0 0 1.25rem}dt{font-weight:bold}dd{margin-left:1.25rem}dd+dt{margin-top:.5rem}.type-summary{border-top:1px solid #8f8f8f;border-left:1px solid #8f8f8f}.type-summary dl{padding:.5rem 0 0 1rem;margin-left:0}.member{font-weight:bold}.member.deprecation-notice{text-decoration:line-through}#sidenav-toggle,#menubutton{display:none}#topnav{position:absolute;top:0;right:0;left:0;height:4rem;background:#424242;color:#fff;font-weight:bold;z-index:1}#topnav>div{position:relative;height:4rem;width:100%}#searchbox{position:absolute;right:1.5rem}#searchbox div{display:table-cell;vertical-align:middle;height:4rem}#searchbox input{width:25rem;box-shadow:inset 1px 2px rgba(0,0,0,0.18);border-radius:15px;border:1px solid #0f0f0f;padding:.2rem .5rem}.ac-renderer{position:absolute;background:#fcfcfc;border:1px solid #424242;-moz-box-shadow:2px 2px 2px rgba(102,102,102,0.4);-webkit-box-shadow:2px 2px 2px rgba(102,102,102,0.4);box-shadow:2px 2px 2px rgba(102,102,102,0.4);z-index:2;width:30rem;overflow:auto}.ac-row{cursor:pointer;padding:.5rem}.ac-highlighted{font-weight:bold}.ac-active{background-color:rgba(82,82,82,0.1)}main{margin-top:4rem;padding-bottom:2.5rem;padding-left:2rem;width:-webkit-calc(75% - 2rem);width:calc(75% - 2rem);float:left}.ctor{padding:.5rem 0 .5rem .5rem}dl.public{border-left:.7rem solid rgba(34,139,34,0.6)}dl.protected{border-left:.7rem solid rgba(218,165,32,0.6)}dl.private{border-left:.7rem solid rgba(255,0,0,0.5)}.wrap-details{border-top:1px solid #8f8f8f;border-left:1px solid #8f8f8f;margin-bottom:1px;display:none}.wrap-details.ctor{padding:0;display:block}main.public .wrap-details.public,main.protected .wrap-details.protected,main.private .wrap-details.private{display:block}.wrap-details.public>div:first-child{border-left:.7rem solid rgba(34,139,34,0.6)}.wrap-details.protected>div:first-child{border-left:.7rem solid rgba(218,165,32,0.6)}.wrap-details.private>div:first-child{border-left:.7rem solid rgba(255,0,0,0.5)}.wrap-details.inv{display:block}main.public .wrap-details.inv.public,main.protected .wrap-details.inv.protected,main.private .wrap-details.inv.private{display:none}.wrap-details.inv.public{color:#828282;font-weight:normal}.wrap-details.inv.public>div:first-child{border-left:.7rem solid rgba(34,139,34,0.4)}.wrap-details.inv.protected{color:#828282;font-weight:normal}.wrap-details.inv.protected>div:first-child{border-left:.7rem solid rgba(218,165,32,0.4)}.wrap-details.inv.private{color:#828282;font-weight:normal}.wrap-details.inv.private>div:first-child{border-left:.7rem solid rgba(255,0,0,0.4)}#visibility-controls{float:right}label{cursor:pointer}label[for=show-public]>span{border:1px outset #228b22;border-radius:5px;background:rgba(34,139,34,0.6);margin:0 .3rem}label[for=show-protected]>span{border:1px outset #daa520;border-radius:5px;background:rgba(218,165,32,0.6);margin:0 .3rem}label[for=show-private]>span{border:1px outset #f00;border-radius:5px;background:rgba(255,0,0,0.5);margin:0 .3rem}details>summary{padding-top:.5rem;margin-left:1.25rem;padding-bottom:.5rem}details>summary p{margin-top:.5rem;margin-bottom:0}details>summary pre:last-child{margin-bottom:0}details.function>summary:before{content:'\2b';float:left;margin-left:-1rem}details.function[open]>summary:before{content:'\2212'}details.function[open]>:last-child{padding-bottom:.5rem}header div.deprecation-notice{margin-top:1rem}h2{font-size:1.8rem;margin-top:1.25rem;margin-bottom:.15rem}section h3{margin:.75rem 0 .25rem;font-size:1.4rem}section+section{margin-top:1.75rem}div.deprecation-notice{font-weight:bold;margin:.25rem 0}h1 span.deprecation-notice,span.deprecation-reason{font-weight:normal;font-style:italic}.info{padding:0 1.25rem}table.srcfile,table.licensefile{border:1px solid #8f8f8f;font-size:1.1rem;width:auto}table.srcfile tr:first-child td,table.licensefile tr:first-child td{padding-top:.5rem}table.srcfile tr:last-child td,table.licensefile tr:last-child td{padding-bottom:.5rem}.licensefile td,.srcfile td,.srcfile a{color:#424242}.licensefile td,.srcfile td{padding:.15rem 1rem;background-color:#f4f4f4}.srcfile td:first-child{text-align:right;padding:0 .5rem 0 1rem;border-right:1px solid #8f8f8f;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:-moz-none;-ms-user-select:none;user-select:none}#sidenav{width:24%;margin-top:4rem;float:right;overflow:hidden;line-height:1.5;font-size:1.2rem;background:#fff}#sidenav input{display:none}#sidenav>a[href]{color:#fff}#sidenav h4{color:#fff;width:100%;font-size:1.4rem;font-weight:500;padding:.5rem 1.2rem 0;height:3rem;margin:0;border-top:1px solid rgba(255,255,255,0.2);border-bottom:1px solid #292929;background:#424242}#sidenav h4:hover{background:#545454}#sidenav li{list-style:none}#sidenav li.link:hover{background:rgba(82,82,82,0.1)}#sidenav li ul{padding:0 0 0 1.5rem}#sidenav ul{margin:0;padding:0 0 0 1rem;overflow:hidden;-webkit-transition:height .25s ease;-moz-transition:height .25s ease;-o-transition:height .25s ease;-ms-transition:height .25s ease;transition:height .25s ease}#main-wrapper{min-height:100%;height:100%;margin-bottom:-3rem}footer,#push-footer{clear:both;height:2rem}footer{font-size:1rem;background:#424242;padding:.5rem 0 .5rem 2.25rem;width:-webkit-calc(100% - 2.25rem);width:calc(100% - 2.25rem)}footer a[href]{color:rgba(255,255,255,0.8)} \ No newline at end of file +@import url(https://fonts.googleapis.com/css?family=Roboto+Mono:400,400italic,700,700italic|Roboto);body,html{min-height:100vh;height:100%}body a[id],h1 a[id],h2 a[id],h3 a[id]{display:inline-block}body,h3 .codelink,h5{font-size:1.4rem}nav .interface,table caption{font-style:italic}*{margin:0;padding:0}html{font-size:10px;position:relative}body{color:#333;font-family:Roboto,sans-serif;line-height:1.6;word-wrap:break-word;background:#f0f0f0;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}body .function,body .property,body a[id]{padding-top:6.4rem;margin-top:-6.4rem}a,a:visited{color:#66f;text-decoration:none}a[href]:hover{text-decoration:underline}h1,h2,h3,h4,h5,h6{margin:1.5rem 0}h1,h2,h3{border-bottom:1px solid #8f8f8f}h1{font-size:2.5rem}h1 .function,h1 .property,h1 a[id]{padding-top:7.5rem;margin-top:-7.5rem}h1+.tags{margin-top:-1rem}h2{font-size:2.2rem;margin-top:4rem}h2 .function,h2 .property,h2 a[id]{padding-top:7.2rem;margin-top:-7.2rem}h3{font-size:1.8rem;border:0}h3 .function,h3 .property,h3 a[id]{padding-top:6.8rem;margin-top:-6.8rem}h3+.tags{margin-top:-1.5rem}h3 .codelink{font-weight:400;display:inline}.tags span,dt{font-weight:700}h3 .codelink:after{clear:none;display:none}h4 a[id],h5 a[id],h6 a[id]{display:inline-block}h3 code{font-size:75%}h4{font-size:1.6rem}h4 .function,h4 .property,h4 a[id]{padding-top:6.6rem;margin-top:-6.6rem}h5 .function,h5 .property,h5 a[id]{padding-top:6.4rem;margin-top:-6.4rem}h6{font-size:1.2rem}h6 .function,h6 .property,h6 a[id]{padding-top:6.2rem;margin-top:-6.2rem}.srcfile table,code,pre.inheritance{font-family:"Roboto Mono",Menlo,monospace;font-size:1.2rem}pre{background:#f8f8f8;border-radius:3px;border-left:.2rem solid #d2d2d2;line-height:1.45;word-wrap:normal;margin:1rem 3rem;padding:1rem;overflow:auto}pre.inheritance{border:0;background:initial;padding:0;margin:.5rem 0 1rem}h1+pre.inheritance{margin-top:-1rem}p{margin:0 0 1rem}li code,p code{background:#f8f8f8;padding:2px;border-radius:3px;border:1px solid #d2d2d2}pre code{border:0}.fn-details div:first-child{border-bottom:1px solid #c2c2c2;margin-top:.5rem}hr.fn-sep{border:0;border-top:1px solid #dcdcdc;width:50%;margin:2rem auto auto}.tags span{background:rgba(102,102,255,.25);border:1px solid #66f;border-radius:3px;color:#66f;font-size:75%;padding:2px 4px;line-height:1.4rem}.tags span+span{margin-left:2px}.function:target>div,.property:target>dl{border-left:3px solid rgba(102,102,255,.7);padding-left:9px;margin-left:-9pt}dt.deprecated{text-decoration:line-through}nav .current,nav .selectable:hover,nav :focus{text-decoration:underline}dt>code{font-weight:400;color:#66f;margin-left:.5rem}.ac-highlighted,.srcfile :target+a,nav .current{font-weight:700}dd{margin:0 0 0 3rem}ol,ul{margin:1rem 0 1rem 4rem}blockquote{margin:1rem 3rem}table{border-collapse:collapse;border-spacing:0;margin:0 auto}table tr{background:#fff;border-top:1px solid #ccc}table tr:nth-child(2n){background:#f8f8f8}td,th{padding:.6rem 1.3rem;border:1px solid #ddd}.parentlink{float:left}.codelink a{float:right}.codelink:after{clear:both;content:"";display:table}footer,header{background:#424242}header{position:fixed;top:0;left:0;right:0;height:5rem;box-shadow:0 1px 5px #888}header>div{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end;height:100%}header input{height:3rem;width:30rem;padding:0 10px;border-radius:15px;border:0;-webkit-appearance:none;color:#fff;background:rgba(255,255,255,.25)}header input::-webkit-input-placeholder{color:#fff}header input::-moz-placeholder{color:#fff}header input:-ms-input-placeholder{color:#fff}header input:focus{background:#fff;color:#333;outline:0}.ac-renderer{position:absolute;background:#fff;border:1px solid #000;box-shadow:2px 2px 2px rgba(102,102,102,.4);z-index:2;overflow:auto;width:32rem;margin-left:-1rem}nav li,nav li label,nav li>a{text-overflow:ellipsis;overflow:hidden}.ac-row{cursor:pointer;padding:.5rem}.ac-active{background-color:rgba(82,82,82,.1)}@media (max-width:320px){.ac-renderer{width:30rem;margin:0}}@media (max-width:1000px){header>div{-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}}@media (min-width:1000px){header>div{width:100rem;margin:0 auto}}footer{padding:0;margin:0;font-size:1.1rem}footer div{display:table-cell;vertical-align:middle;height:4rem}footer a{padding-left:2.25rem}footer a,footer a:visited{color:rgba(255,255,255,.8)}#nav-modules,#nav-modules:checked~div,#nav-types,#nav-types:checked~div{display:none}#nav-modules~label h3:after,#nav-types~label h3:after{content:'\2335';display:inline-block;float:right;margin-right:1rem;-webkit-transform:rotate(0);transform:rotate(0)}#nav-modules:checked~label h3:after,#nav-types:checked~label h3:after{float:right;margin-right:1rem;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}nav{margin:0 0 3rem 1rem;font-size:1.3rem;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}nav .selectable,nav a{-webkit-touch-callout:default;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}nav :focus{outline:0}nav ul{margin:0;padding:0}nav ul.nav-tree{margin:0 0 0 .3rem;padding:0 0 0 .5rem;border-left:1px dotted rgba(66,66,66,.5)}nav li{list-style:none;word-wrap:initial}nav li input{display:none}nav li input~ul.nav-tree>li{display:block}nav li input:checked~ul.nav-tree>li{display:none}nav li label,nav li>a{display:block;width:100%;white-space:nowrap}nav li label.focused,nav li label:hover,nav li>a:focus,nav li>a:hover{background:rgba(102,102,255,.1)}nav li>a:before{visibility:hidden}nav li label:before,nav li>a:before{float:left;margin-right:1rem;content:'\2212';font-size:1.4rem}nav li input:checked+label:before{content:'\002b'}nav h3{font-size:2rem;border:0;border-top:1px solid #8f8f8f;margin-top:1.2rem}nav h3 a,nav h3 a:visited{color:#424242}nav>h3:first-child{border-top:0;margin-top:0}div.pre-footer>div,main{background:#fff}main{-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row-reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}article{margin-top:2.5rem;padding:3rem}article.indexfile>h1,article.srcfile>h1{margin-top:0;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}article.indexfile>h1:hover,article.srcfile>h1:hover{overflow:visible}.srcfile div{width:100%;overflow:auto}.srcfile table{background:#f8f8f8;border-radius:3px;border-left:.2rem solid #d2d2d2;line-height:1.45;word-wrap:normal;white-space:pre;margin:1rem 0;width:100%}.srcfile td{padding:0 .8rem;border:0}.srcfile td:first-child{text-align:right;padding-left:.8rem}.srcfile tr{background:#f8f8f8;border:0}.srcfile tr:first-child td{padding-top:1rem}.srcfile tr:last-child td{padding-bottom:1rem}.srcfile tr.hilite{background:rgba(102,102,255,.1)}@media (max-width:1000px){main{-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}article{padding:3rem 1rem}}@media (min-width:1000px){main{width:100rem;margin:0 auto}article{width:66rem}nav{margin-top:2.5rem;width:23rem;float:left;padding-top:3rem;padding-right:4rem}} \ No newline at end of file diff --git a/docs/dossier.js b/docs/dossier.js index b2ebb52..9fdece5 100644 --- a/docs/dossier.js +++ b/docs/dossier.js @@ -1,135 +1,86 @@ -(function(){var h,m=this;function aa(){} -function ba(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"== -b&&"undefined"==typeof a.call)return"object";return b}function r(a){return"array"==ba(a)}function ca(a){var b=ba(a);return"array"==b||"object"==b&&"number"==typeof a.length}function s(a){return"string"==typeof a}function da(a){return"number"==typeof a}function ea(a){return"function"==ba(a)}function fa(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ga(a){return a[ha]||(a[ha]=++ia)}var ha="closure_uid_"+(1E9*Math.random()>>>0),ia=0; -function ja(a,b,c){return a.call.apply(a.bind,arguments)}function ka(a,b,c){if(!a)throw Error();if(2")&&(a=a.replace(sa,">"));-1!=a.indexOf('"')&&(a=a.replace(ta,"""));return a}var qa=/&/g,ra=//g,ta=/\"/g,pa=/[&<>\"]/;function ua(a){return String(a).replace(/([-()\[\]{}+?*.$\^|,:#c?Math.max(0,a.length+c):c;if(s(a))return s(b)&&1==b.length?a.indexOf(b,c):-1;for(;cc?null:s(a)?a.charAt(c):a[c]}function Aa(a){return z.concat.apply(z,arguments)} -function Ba(a){var b=a.length;if(0=arguments.length?z.slice.call(a,b):z.slice.call(a,b,c)};var Ea,Fa,Ga,Ha,B;function Ia(){return m.navigator?m.navigator.userAgent:null}function Ja(){return m.navigator}Ha=Ga=Fa=Ea=!1;var Ka;if(Ka=Ia()){var La=Ja();Ea=0==Ka.lastIndexOf("Opera",0);Fa=!Ea&&(-1!=Ka.indexOf("MSIE")||-1!=Ka.indexOf("Trident"));Ga=!Ea&&-1!=Ka.indexOf("WebKit");Ha=!Ea&&!Ga&&!Fa&&"Gecko"==La.product}var C=Ea,D=Fa,E=Ha,F=Ga,Ma=Ja();B=-1!=(Ma&&Ma.platform||"").indexOf("Mac");var Na=!!Ja()&&-1!=(Ja().appVersion||"").indexOf("X11"); -function Oa(){var a=m.document;return a?a.documentMode:void 0}var Pa;a:{var Qa="",Ra;if(C&&m.opera)var Sa=m.opera.version,Qa="function"==typeof Sa?Sa():Sa;else if(E?Ra=/rv\:([^\);]+)(\)|;)/:D?Ra=/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/:F&&(Ra=/WebKit\/(\S+)/),Ra)var Ta=Ra.exec(Ia()),Qa=Ta?Ta[1]:"";if(D){var Ua=Oa();if(Ua>parseFloat(Qa)){Pa=String(Ua);break a}}Pa=Qa}var Va={}; -function G(a){var b;if(!(b=Va[a])){b=0;for(var c=na(String(Pa)).split("."),d=na(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f(0==q[1].length?0:parseInt(q[1],10))?1:0)||((0==p[2].length)<(0==q[2].length)?-1: -(0==p[2].length)>(0==q[2].length)?1:0)||(p[2]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=c.join("")}c=a.createElement(c);d&&(s(d)?c.className=d:r(d)?Za.apply(null,[c].concat(d)):hb(c,d));2"+d,c.removeChild(c.firstChild)):c.innerHTML=d;if(1==c.childNodes.length)d=c.removeChild(c.firstChild);else for(d=e.createDocumentFragment();c.firstChild;)d.appendChild(c.firstChild);return d}function vb(a){return fa(a)?"zSoyz":String(a)}var wb={};/* - - Copyright 2008 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. -*/ -function N(a){return a&&a.va&&a.va===sb?a.content:String(a).replace(xb,yb)}var zb={"\x00":"�",'"':""","&":"&","'":"'","<":"<",">":">","\t":" ","\n":" ","\x0B":" ","\f":" ","\r":" "," ":" ","-":"-","/":"/","=":"=","`":"`","\u0085":"…","\u00a0":" ","\u2028":"
","\u2029":"
"};function yb(a){return zb[a]}var xb=/[\x00\x22\x26\x27\x3c\x3e]/g;function Ab(a){return'
        "} -function Bb(a){var b="";if(a.types.length){for(var b=b+""}return b} -function Cb(a){var b,c=0");if(c){b+=a.file.name?"
      • "+N(a.file.name)+"/
          ":"";for(var c=a.file.children,d=c.length,e=0;e":""}else a.file.name&&(b+='
        "};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);-1e.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.currentTarget;f;f=f.parentNode)e.push(f);for(var f=a.type,k=e.length-1;!c.G&&0<=k;k--)c.currentTarget=e[k],d&=bc(e[k],f,!0,c);for(k=0;!c.G&&k>>0);function Wb(a){return ea(a)?a:a[dc]||(a[dc]=function(b){return a.handleEvent(b)})};var ec=!!m.DOMTokenList,fc=ec?function(a){return a.classList}:function(a){a=a.className;return s(a)&&a.match(/\S+/g)||[]},S=ec?function(a,b){return a.classList.contains(b)}:function(a,b){var c=fc(a);return 0<=va(c,b)},gc=ec?function(a,b){a.classList.add(b)}:function(a,b){S(a,b)||(a.className+=0=this.left&&a.right<=this.right&&a.top>=this.top&&a.bottom<=this.bottom:a.x>=this.left&&a.x<=this.right&&a.y>=this.top&&a.y<=this.bottom:!1}; -T.prototype.round=function(){this.top=Math.round(this.top);this.right=Math.round(this.right);this.bottom=Math.round(this.bottom);this.left=Math.round(this.left);return this};function kc(a,b,c,d){this.left=a;this.top=b;this.width=c;this.height=d}kc.prototype.X=function(){return new kc(this.left,this.top,this.width,this.height)};kc.prototype.contains=function(a){return a instanceof kc?this.left<=a.left&&this.left+this.width>=a.left+a.width&&this.top<=a.top&&this.top+this.height>=a.top+a.height:a.x>=this.left&&a.x<=this.left+this.width&&a.y>=this.top&&a.y<=this.top+this.height}; -kc.prototype.round=function(){this.left=Math.round(this.left);this.top=Math.round(this.top);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function U(a,b){var c=K(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,null))?c[b]||c.getPropertyValue(b)||"":""}function V(a,b){return U(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style&&a.style[b]} -function lc(a){var b;try{b=a.getBoundingClientRect()}catch(c){return{left:0,top:0,right:0,bottom:0}}D&&a.ownerDocument.body&&(a=a.ownerDocument,b.left-=a.documentElement.clientLeft+a.body.clientLeft,b.top-=a.documentElement.clientTop+a.body.clientTop);return b} -function mc(a){if(D&&!(D&&8<=H))return a.offsetParent;var b=K(a),c=V(a,"position"),d="fixed"==c||"absolute"==c;for(a=a.parentNode;a&&a!=b;a=a.parentNode)if(c=V(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return null} -function nc(a){for(var b=new T(0,Infinity,Infinity,0),c=J(a),d=c.j.body,e=c.j.documentElement,f=jb(c.j);a=mc(a);)if(!(D&&0==a.clientWidth||F&&0==a.clientHeight&&a==d||a==d||a==e||"visible"==V(a,"overflow"))){var g=W(a),k;k=a;if(E&&!G("1.9")){var l=parseFloat(U(k,"borderLeftWidth"));if(oc(k))var n=k.offsetWidth-k.clientWidth-l-parseFloat(U(k,"borderRightWidth")),l=l+n;k=new I(l,parseFloat(U(k,"borderTopWidth")))}else k=new I(k.clientLeft,k.clientTop);g.x+=k.x;g.y+=k.y;b.top=Math.max(b.top,g.y);b.right= -Math.min(b.right,g.x+a.clientWidth);b.bottom=Math.min(b.bottom,g.y+a.clientHeight);b.left=Math.max(b.left,g.x)}d=f.scrollLeft;f=f.scrollTop;b.left=Math.max(b.left,d);b.top=Math.max(b.top,f);c=(c.j.parentWindow||c.j.defaultView||window).document;c="CSS1Compat"==c.compatMode?c.documentElement:c.body;c=new ab(c.clientWidth,c.clientHeight);b.right=Math.min(b.right,d+c.width);b.bottom=Math.min(b.bottom,f+c.height);return 0<=b.top&&0<=b.left&&b.bottom>b.top&&b.right>b.left?b:null} -function W(a){var b,c=K(a),d=V(a,"position"),e=E&&c.getBoxObjectFor&&!a.getBoundingClientRect&&"absolute"==d&&(b=c.getBoxObjectFor(a))&&(0>b.screenX||0>b.screenY),f=new I(0,0),g;b=c?K(c):document;g=!D||D&&9<=H||pb(J(b))?b.documentElement:b.body;if(a==g)return f;if(a.getBoundingClientRect)b=lc(a),a=qb(J(c)),f.x=b.left+a.x,f.y=b.top+a.y;else if(c.getBoxObjectFor&&!e)b=c.getBoxObjectFor(a),a=c.getBoxObjectFor(g),f.x=b.screenX-a.screenX,f.y=b.screenY-a.screenY;else{b=a;do{f.x+=b.offsetLeft;f.y+=b.offsetTop; -b!=a&&(f.x+=b.clientLeft||0,f.y+=b.clientTop||0);if(F&&"fixed"==V(b,"position")){f.x+=c.body.scrollLeft;f.y+=c.body.scrollTop;break}b=b.offsetParent}while(b&&b!=a);if(C||F&&"absolute"==d)f.y-=c.body.offsetTop;for(b=a;(b=mc(b))&&b!=c.body&&b!=g;)f.x-=b.scrollLeft,C&&"TR"==b.tagName||(f.y-=b.scrollTop)}return f}function pc(a,b){"number"==typeof a&&(a=(b?Math.round(a):a)+"px");return a} -function qc(a){var b=rc;if("none"!=V(a,"display"))return b(a);var c=a.style,d=c.display,e=c.visibility,f=c.position;c.visibility="hidden";c.position="absolute";c.display="inline";a=b(a);c.display=d;c.position=f;c.visibility=e;return a}function rc(a){var b=a.offsetWidth,c=a.offsetHeight,d=F&&!b&&!c;return(void 0===b||d)&&a.getBoundingClientRect?(a=lc(a),new ab(a.right-a.left,a.bottom-a.top)):new ab(b,c)}function sc(a,b){a.style.display=b?"":"none"}function oc(a){return"rtl"==V(a,"direction")} -var tc=E?"MozUserSelect":F?"WebkitUserSelect":null;function uc(a){var b=a.getElementsByTagName("*");if(tc){var c="none";a.style[tc]=c;if(b){a=0;for(var d;d=b[a];a++)d.style[tc]=c}}else if(D||C)if(c="on",a.setAttribute("unselectable",c),b)for(a=0;d=b[a];a++)d.setAttribute("unselectable",c)} -function vc(a,b){if(/^\d+px?$/.test(b))return parseInt(b,10);var c=a.style.left,d=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;a.style.left=b;var e=a.style.pixelLeft;a.style.left=c;a.runtimeStyle.left=d;return e}function wc(a,b){var c=a.currentStyle?a.currentStyle[b]:null;return c?vc(a,c):0}var xc={thin:2,medium:4,thick:6}; -function yc(a,b){if("none"==(a.currentStyle?a.currentStyle[b+"Style"]:null))return 0;var c=a.currentStyle?a.currentStyle[b+"Width"]:null;return c in xc?xc[c]:vc(a,c)}function zc(a){if(D&&!(D&&9<=H)){var b=yc(a,"borderLeft"),c=yc(a,"borderRight"),d=yc(a,"borderTop");a=yc(a,"borderBottom");return new T(d,c,a,b)}b=U(a,"borderLeftWidth");c=U(a,"borderRightWidth");d=U(a,"borderTopWidth");a=U(a,"borderBottomWidth");return new T(parseFloat(d),parseFloat(c),parseFloat(a),parseFloat(b))} -var Ac=/[^\d]+$/,Bc={cm:1,"in":1,mm:1,pc:1,pt:1},Cc={em:1,ex:1};function Dc(){var a=document.documentElement,b=V(a,"fontSize"),c;c=(c=b.match(Ac))&&c[0]||null;if(b&&"px"==c)return parseInt(b,10);if(D){if(c in Bc)return vc(a,b);if(a.parentNode&&1==a.parentNode.nodeType&&c in Cc)return a=a.parentNode,c=V(a,"fontSize"),vc(a,b==c?"1em":b)}c=kb("span",{style:"visibility:hidden;position:absolute;line-height:0;padding:0;margin:0;border:0;height:1em;"});a.appendChild(c);b=c.offsetHeight;ob(c);return b} -var Ec=/matrix\([0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, ([0-9\.\-]+)p?x?, ([0-9\.\-]+)p?x?\)/;function Fc(a){this.c=a||[];this.Ub=!0}function Gc(a,b,c){var d=[];if(""!=a){a=ua(a);a=RegExp("(^|\\W+)"+a,"i");for(var e=0;ep?(p=u-p-1,p>q-5&&(p=q-5),l+=p,p=u):(l+=q,q+=5);l<6*g.length&&d.push({Rb:f,ob:l,index:e})}d.sort(function(a,b){var c=a.ob-b.ob;return 0!=c?c:a.index-b.index});a=[];for(y=0;y=a.k&&ca.k)c--;else if(a.Ma&&c==a.k){a.A(-1);break}else if(!a.tb||-1!=c&&c!=a.k)break;else c=b;if(a.A(c))break}}h.A=function(a){var b=Oc(this,a),c=this.c[b];return c&&this.R.za&&this.R.za(c)?!1:(this.D=a,this.p.A(a),-1!=b)}; -function Pc(a){var b=Oc(a,a.D);if(-1!=b){var b=a.c[b],c=a.aa,d=b.toString();if(c.S){var e=Uc(c,c.a.value,Vc(c.a)),f=Wc(c,c.a.value);c.Ob.test(d)||(d=d.replace(/[\s\xa0]+$/,"")+c.yb);c.Wb&&(0==e||/^[\s\xa0]*$/.test(f[e-1])||(d=" "+d),e==f.length-1&&(d+=" "));if(d!=f[e]){f[e]=d;d=c.a;(E||D&&G("9"))&&d.blur();d.value=f.join("");for(var g=0,k=0;k<=e;k++)g+=f[k].length;d.focus();e=g;f=c.a;d=e;Xc(f)?f.selectionStart=d:D&&(g=Yc(f),k=g[0],k.inRange(g[1])&&(d=Zc(f,d),k.collapse(!0),k.move("character",d),k.select())); -f=c.a;Xc(f)?f.selectionEnd=e:D&&(g=Yc(f),d=g[1],g[0].inRange(d)&&(e=Zc(f,e),f=Zc(f,Vc(f)),d.collapse(!0),d.moveEnd("character",e-f),d.select()))}}else c.a.value=d;c.Ja=!0;a.qb?(a.q=null,Rc(a)):a.u();a.dispatchEvent({type:"update",V:b});a.qb&&a.aa.update(!0);return!0}a.u();a.dispatchEvent({type:"update",V:null});return!1}h.u=function(){this.D=-1;this.q=null;this.k+=this.c.length;this.c=[];window.clearTimeout(this.J);this.J=null;this.p.u();this.dispatchEvent("suggestionsupdate");this.dispatchEvent(Nc)}; -function Rc(a){a.J||(a.J=window.setTimeout(t(a.u,a),100))}h.Ua=function(){return this.J?(window.clearTimeout(this.J),this.J=null,!0):!1};function Qc(a){a.Ua()||window.setTimeout(t(a.Ua,a),10)}h.e=function(){Jc.i.e.call(this);delete this.Va;this.p.B();this.aa.B();this.R=null};h.Ib=function(a,b,c){this.q==a&&this.Ha(b,c)}; -h.Ha=function(a,b){var c="object"==ba(b)&&b,d=(c?c.Yb():b)?Oc(this,this.D):-1;this.k+=this.c.length;this.c=a;for(var e=[],f=0;fc||c>=a.c.length?-1:c} -h.ta=function(a){var b=this.aa;b.ta.apply(b,arguments)};h.update=function(a){this.aa.update(a)};function $c(a,b){X.call(this);this.Q=a||1;this.W=b||m;this.ua=t(this.Sb,this);this.Ca=w()}x($c,X);h=$c.prototype;h.enabled=!1;h.f=null;h.Sb=function(){if(this.enabled){var a=w()-this.Ca;0=a||96<=a&&106>=a||65<=a&&90>=a||F&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;default:return!1}}function jd(a){if(E)a=kd(a);else if(B&&F)a:switch(a){case 93:a=91;break a}return a} -function kd(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};function ld(a,b){X.call(this);a&&md(this,a,b)}x(ld,X);h=ld.prototype;h.b=null;h.ia=null;h.Aa=null;h.ja=null;h.m=-1;h.F=-1;h.sa=!1; -var nd={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},od={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},pd=D||F&&G("525"),qd=B&&E;h=ld.prototype; -h.Eb=function(a){F&&(17==this.m&&!a.ctrlKey||18==this.m&&!a.altKey||B&&91==this.m&&!a.metaKey)&&(this.F=this.m=-1);-1==this.m&&(a.ctrlKey&&17!=a.keyCode?this.m=17:a.altKey&&18!=a.keyCode?this.m=18:a.metaKey&&91!=a.keyCode&&(this.m=91));pd&&!hd(a.keyCode,this.m,a.shiftKey,a.ctrlKey,a.altKey)?this.handleEvent(a):(this.F=jd(a.keyCode),qd&&(this.sa=a.altKey))};h.Gb=function(a){this.F=this.m=-1;this.sa=a.altKey}; -h.handleEvent=function(a){var b=a.O,c,d,e=b.altKey;D&&"keypress"==a.type?(c=this.F,d=13!=c&&27!=c?b.keyCode:0):F&&"keypress"==a.type?(c=this.F,d=0<=b.charCode&&63232>b.charCode&&id(c)?b.charCode:0):C?(c=this.F,d=id(c)?b.keyCode:0):(c=b.keyCode||this.F,d=b.charCode||0,qd&&(e=this.sa),B&&63==d&&224==c&&(c=191));var f=c=jd(c),g=b.keyIdentifier;c?63232<=c&&c in nd?f=nd[c]:25==c&&a.shiftKey&&(f=9):g&&g in od&&(f=od[g]);a=f==this.m;this.m=f;b=new rd(f,d,a,b);b.altKey=e;this.dispatchEvent(b)}; -function md(a,b,c){a.ja&&a.detach();a.b=b;a.ia=R(a.b,"keypress",a,c);a.Aa=R(a.b,"keydown",a.Eb,c,a);a.ja=R(a.b,"keyup",a.Gb,c,a)}h.detach=function(){this.ia&&(ac(this.ia),ac(this.Aa),ac(this.ja),this.ja=this.Aa=this.ia=null);this.b=null;this.F=this.m=-1};h.e=function(){ld.i.e.call(this);this.detach()};function rd(a,b,c,d){d&&Kb(this,d,void 0);this.type="key";this.keyCode=a;this.charCode=b;this.repeat=c}x(rd,Q);var sd,td;td=sd=!1;var ud=Ia();ud&&(-1!=ud.indexOf("Firefox")||-1!=ud.indexOf("Camino")||(-1!=ud.indexOf("iPhone")||-1!=ud.indexOf("iPod")?sd=!0:-1!=ud.indexOf("iPad")&&(td=!0)));var vd=sd,wd=td;function xd(a,b,c,d){O.call(this);d=d||150;this.S=null!=c?c:!0;this.ba=a||",;";this.yb=this.ba.substring(0,1);a=this.S?"[\\s"+this.ba+"]+":"[\\s]+";this.rb=RegExp("^"+a+"|"+a+"$","g");this.Ob=RegExp("\\s*["+this.ba+"]$");this.Za=b||"";this.Lb=this.S;this.f=0=d.right)&&(k&=-2),132==(k&132)&&(g.y=d.bottom)&&(k&=-5),g.xd.right&&k&16&&(e.width=Math.max(e.width-(g.x+e.width-d.right),0),n|=4),g.x+e.width>d.right&&k&1&&(g.x=Math.max(d.right-e.width,d.left),n|=1),k&2&&(n=n|(g.xd.right?32:0)),g.y=d.top&&g.y+e.height>d.bottom&&k&32&&(e.height=Math.max(e.height-(g.y+e.height-d.bottom),0),n|=8),g.y+e.height>d.bottom&&k&4&&(g.y=Math.max(d.bottom-e.height,d.top),n|=2),k&8&&(n=n|(g.yd.bottom?128:0)),d=n):d=256;d&496||(g=f,f=E&&(B||Na)&&G("1.9"),g instanceof I?(d=g.x,g=g.y):(d=g,g=void 0),c.style.left= -pc(d,f),c.style.top=pc(g,f),b==e||b&&e&&b.width==e.width&&b.height==e.height||(d=pb(J(K(c))),!D||d&&G("8")?(c=c.style,E?c.MozBoxSizing="border-box":F?c.WebkitBoxSizing="border-box":c.boxSizing="border-box",c.width=Math.max(e.width,0)+"px",c.height=Math.max(e.height,0)+"px"):(b=c.style,d?(D?(d=wc(c,"paddingLeft"),f=wc(c,"paddingRight"),g=wc(c,"paddingTop"),k=wc(c,"paddingBottom"),d=new T(g,f,k,d)):(d=U(c,"paddingLeft"),f=U(c,"paddingRight"),g=U(c,"paddingTop"),k=U(c,"paddingBottom"),d=new T(parseFloat(g), -parseFloat(f),parseFloat(k),parseFloat(d))),c=zc(c),b.pixelWidth=e.width-c.left-d.left-d.right-c.right,b.pixelHeight=e.height-c.top-d.top-d.bottom-c.bottom):(b.pixelWidth=e.width,b.pixelHeight=e.height))));a.oa&&(a.b.style.visibility="visible")}}h.e=function(){this.b&&($b(this.b,"click",this.Qa,!1,this),$b(this.b,"mousedown",this.Sa,!1,this),$b(this.b,"mouseover",this.Ta,!1,this),this.w.removeNode(this.b),this.b=null,this.t=!1);Hb(this.N);this.Ga=null;Rd.i.e.call(this)}; -function Vd(a,b,c){if(3==b.nodeType){var d=null;r(c)&&1w()-this.pb)&&this.dispatchEvent({type:Kc,V:this.c[a].id})};function Zd(a,b){var c=new Fc(a),d=new Rd,e=new xd(null,null,!1),c=new Jc(c,d,e);e.d=c;e.ta(b);return c};/* - Copyright 2013 Jason Leyba - - 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. -*/ -function $d(){var a=m.TYPES;ae(a);M("type-index")&&M("file-index")&&M("module-index")&&(be("file-index",a.files),ce("type-index",a.types),ce("module-index",a.modules,!0));de();setTimeout(la(ee,a),0);setTimeout(fe,0)}var ge=["init"],$=m;ge[0]in $||!$.execScript||$.execScript("var "+ge[0]);for(var he;ge.length&&(he=ge.shift());)ge.length||void 0===$d?$=$[he]?$[he]:$[he]={}:$[he]=$d; -var ie=function(){var a="./";za(document.getElementsByTagName("script"),function(b){b=b.src;var c=b.length;return"dossier.js"===b.substr(c-10)?(a=b.substr(0,c-10),!0):!1});return a}(); -function ee(a){function b(){var a=c[e.value];a&&(window.location.href=ie+a)}var c={},d=Aa(ya(a.files,function(a){var b="file://"+a.name;c[b]=a.href;return b}),ya(a.types,function(a){c[a.name]=a.href;return a.name}));A(a.modules,function(a){c[a.name]=a.href;d=Aa(d,a.name,ya(a.types||[],function(b){var d=a.name+"."+b.name;c[d]=b.href;return d}))});a=M("searchbox");R(a,"submit",function(a){a.preventDefault();a.stopPropagation();b();return!1});var e=a.getElementsByTagName("input")[0];a=Zd(d,e);a.$a=15; -R(a,"update",b)} -function ae(a){function b(a,b,c,d){R(a,Ib,function(){b.style.height=pc(a.checked?c:0,!0);je(d,a)})}if(M("sidenav")){M("sidenav-overview").href=ie+"index.html";var c=M("sidenav-types-ctrl"),d=ce("sidenav-types",a.types),e=M("sidenav-modules-ctrl"),f=ce("sidenav-modules",a.modules),g=M("sidenav-files-ctrl");a=be("sidenav-files",a.files);var k=Dc(),l=qc(d).height/k+"rem",n=qc(a).height/k+"rem",k=qc(f).height/k+"rem";c.checked=ke("dossier.typesList");g.checked=ke("dossier.filesList");e.checked=ke("dossier.modulesList"); -d.style.height=pc(c.checked?l:0,!0);a.style.height=pc(g.checked?n:0,!0);f.style.height=pc(e.checked?k:0,!0);b(c,d,l,"dossier.typesList");b(g,a,n,"dossier.filesList");b(e,f,k,"dossier.modulesList")}}function le(a,b){this.name=a;this.href=b||"";this.children=[]} -function me(a){function b(a){A(a.children,b);!a.href&&1===a.children.length&&a.children[0].children.length&&(a.name=(a.name?a.name+"/":"")+a.children[0].name,a.href=a.children[0].href,a.children=a.children[0].children)}var c=new le("");A(a,function(a){var b=a.name.split(/[\/\\]/),f=c;A(b,function(c,k){if(c)if(k===b.length-1)f.children.push(new le(c,a.href));else{var l=za(f.children,function(a){return a.name===c});l||(l=new le(c),f.children.push(l));f=l}})});b(c);return c} -function be(a,b){var c=M(a),d=c.querySelector("i");if(!b.length)return d;var e=me(b),e=ub(Cb,{file:e,I:ie});ob(d);c.appendChild(e);return e}function ce(a,b,c){a=M(a);var d=a.querySelector("i");return(b=ub(Bb,{types:b,I:ie,ya:!!c}))&&b.childNodes.length?(ob(d),a.appendChild(b),b):d} -function fe(){var a=document.getElementsByTagName("DETAILS");a.length&&!a[0].hasOwnProperty("open")&&A(a,function(a){function c(){(d=!d)?a.setAttribute("open",""):a.removeAttribute("open");A(a.childNodes,function(a){1===a.nodeType&&"SUMMARY"!==a.tagName.toUpperCase()&&sc(a,d)})}var d=!0;a.setAttribute("open","");R(a,"click",c);c()})}function je(a,b){window.localStorage&&(b.checked?window.localStorage.setItem(a,"1"):window.localStorage.removeItem(a))} -function ke(a){return window.localStorage?!!window.localStorage.getItem(a):!1}var ne="public",oe="protected",pe="private";function qe(a){return"dossier.visibility."+a} -function de(){function a(){var a=window.localStorage;a&&!a.getItem("dossier.visibility")&&(a.setItem("dossier.visibility","1"),a.setItem(qe(ne),"1"),a.removeItem(qe(oe)),a.removeItem(qe(pe)))}function b(a,b){var d=document.getElementById(a);if(d){var e=qe(b);d.checked=ke(e);c(d,b);R(d,Ib,function(){je(e,d);c(d,b)})}}function c(a,b){a.checked?gc(f,b):ic(f,b)}function d(a){function b(){for(var a=[],e=d=f=y=0,k=c.length;e>>0),ea=0;function fa(a,b,c){return a.call.apply(a.bind,arguments)} +function ga(a,b,c){if(!a)throw Error();if(2")&&(a=a.replace(za,">"));-1!=a.indexOf('"')&&(a=a.replace(Aa,"""));-1!=a.indexOf("'")&&(a=a.replace(Ba,"'"));-1!=a.indexOf("\x00")&&(a=a.replace(Ca,"�"));return a}var xa=/&/g,ya=//g,Aa=/"/g,Ba=/'/g,Ca=/\x00/g,wa=/[\x00&<>"']/; +function Da(a){return String(a).replace(/([-()\[\]{}+?*.$\^|,:#b?1:0};var B=Array.prototype,Fa=B.indexOf?function(a,b,c){return B.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(t(a))return t(b)&&1==b.length?a.indexOf(b,c):-1;for(;cc?null:t(a)?a.charAt(c):a[c]}function Ia(a,b){var c=Fa(a,b),d;(d=0<=c)&&B.splice.call(a,c,1);return d}function Ja(a){var b=a.length;if(0=arguments.length?B.slice.call(a,b):B.slice.call(a,b,c)};function La(a,b,c,d){var e=document.createElement("li"),f=document.createElement("a");f.classList.add("nav-item");f.textContent=a.getKey();f.tabIndex=2;a.b&&(f.href=b+a.b.href,a.b.interface&&f.classList.add("interface"));var g=a.G();if(g){var h=document.createElement("input");h.type="checkbox";h.id=d+a.getKey();e.appendChild(h);d=document.createElement("label");d.setAttribute("for",h.id);d.appendChild(f);a.b&&a.b.href===c&&d.classList.add("current");e.appendChild(d);var k=document.createElement("ul"); +k.classList.add("nav-tree");e.appendChild(k);g.forEach(function(a){k.appendChild(La(a,b,c,h.id+"."))})}else a.b&&a.b.href===c&&f.classList.add("current"),e.appendChild(f);return e}function Ma(a){if(a=a.G())a.sort(function(a,c){return a.getKey()c.getKey()?1:0}),a.forEach(Ma)} +function Na(a){var b=a.G();b&&(b.forEach(Na),b.filter(function(a){return null===a.b&&1==(a.a?a.a.length:0)}).forEach(function(b){a.removeChild(b);var d=Oa(b);d&&d.forEach(function(d){var f=b.getKey()+"."+d.getKey();d.f=f;Pa(a,d)})}),b.filter(function(a){return a.b&&!a.b.namespace&&!a.b.types&&(a.a?a.a.length:0)}).forEach(function(b){var d=Oa(b);d&&d.forEach(function(d){var f=b.getKey()+"."+d.getKey();d.f=f;Pa(a,d)})}))}function D(a,b){this.f=a;this.b=b;this.a=this.c=null}D.prototype.getKey=function(){return this.f}; +D.prototype.R=function(a){this.b=a};function Pa(a,b){a.a||(a.a=[]);b.c=a;a.a.push(b)}D.prototype.G=function(){return this.a};D.prototype.removeChild=function(a){a.c=null;Ia(this.a,a)};function Oa(a){var b=a.a;a.a&&(a.a.forEach(function(a){a.c=null}),a.a=null);return b}function Qa(a,b){return a.a?Ha(a.a,function(a){return a.f===b}):null} +function Ra(a,b){var c=new D("",null);a.forEach(function(a){if(b){var e;a.types?(e=Ra(a.types,!1),e.f=a.name,e.R(a)):e=new D(a.name,a);Pa(c,e)}else{var f=c;a.name.split(/\./).forEach(function(a){var b=Qa(f,a);b||(b=new D(a,null),Pa(f,b));f=b});f.R(a)}});Na(c);Ma(c);return c}function Sa(a,b,c){var d=Ta;d&&!ta(d,"/")&&(d+="/");a=Ra(a,c);var e=document.createElement("ul");a.G()&&a.G().forEach(function(a){e.appendChild(La(a,d,b,c?".nav-module:":".nav:"))});return e};function Ua(a){if(a.classList)return a.classList;a=a.className;return t(a)&&a.match(/\S+/g)||[]}function Va(a,b){var c;a.classList?c=a.classList.contains(b):(c=Ua(a),c=0<=Fa(c,b));return c}function Wa(a,b){a.classList?a.classList.add(b):Va(a,b)||(a.className+=0=this.left&&a.right<=this.right&&a.top>=this.top&&a.bottom<=this.bottom:a.x>=this.left&&a.x<=this.right&&a.y>=this.top&&a.y<=this.bottom:!1}; +l.ceil=function(){this.top=Math.ceil(this.top);this.right=Math.ceil(this.right);this.bottom=Math.ceil(this.bottom);this.left=Math.ceil(this.left);return this};l.floor=function(){this.top=Math.floor(this.top);this.right=Math.floor(this.right);this.bottom=Math.floor(this.bottom);this.left=Math.floor(this.left);return this};l.round=function(){this.top=Math.round(this.top);this.right=Math.round(this.right);this.bottom=Math.round(this.bottom);this.left=Math.round(this.left);return this};function kb(a,b,c,d){this.left=a;this.top=b;this.width=c;this.height=d}l=kb.prototype;l.clone=function(){return new kb(this.left,this.top,this.width,this.height)};l.contains=function(a){return a instanceof kb?this.left<=a.left&&this.left+this.width>=a.left+a.width&&this.top<=a.top&&this.top+this.height>=a.top+a.height:a.x>=this.left&&a.x<=this.left+this.width&&a.y>=this.top&&a.y<=this.top+this.height}; +l.ceil=function(){this.left=Math.ceil(this.left);this.top=Math.ceil(this.top);this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};l.floor=function(){this.left=Math.floor(this.left);this.top=Math.floor(this.top);this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};l.round=function(){this.left=Math.round(this.left);this.top=Math.round(this.top);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function lb(a){this.a=a||[]}function mb(a,b,c){for(var d=[],e=0;ep?(p=da-p-1,p>w-5&&(p=w-5),k+=p,p=da):(k+=w,w+=5);k<6*g.length&&d.push({Da:f,ka:k,index:e})}d.sort(function(a,b){var c=a.ka-b.ka;return 0!=c?c:a.index-b.index});a=[];for(x=0;xparseFloat(a))?String(b):a}(),rb={}; +function N(a){var b;if(!(b=rb[a])){b=0;for(var c=ua(String(qb)).split("."),d=ua(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f=a)}var tb=n.document,ub=pb(),sb=!tb||!J||!ub&&F()?void 0:ub||("CSS1Compat"==tb.compatMode?parseInt(qb,10):5);var vb=!J||O(9),wb=!K&&!J||J&&O(9)||K&&N("1.9.1");J&&N("9");function P(a){return a?new xb(Q(a)):ka||(ka=new xb)}function yb(a){var b=document;return t(a)?b.getElementById(a):a}function zb(a,b){pa(b,function(b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in Ab?a.setAttribute(Ab[d],b):0==d.lastIndexOf("aria-",0)||0==d.lastIndexOf("data-",0)?a.setAttribute(d,b):a[d]=b})} +var Ab={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",frameborder:"frameBorder",height:"height",maxlength:"maxLength",role:"role",rowspan:"rowSpan",type:"type",usemap:"useMap",valign:"vAlign",width:"width"};function Bb(a){var b=Cb(a);a=Db(a);return J&&N("10")&&a.pageYOffset!=b.scrollTop?new G(b.scrollLeft,b.scrollTop):new G(a.pageXOffset||b.scrollLeft,a.pageYOffset||b.scrollTop)} +function Cb(a){return L||"CSS1Compat"!=a.compatMode?a.body||a.documentElement:a.documentElement}function Db(a){return a.parentWindow||a.defaultView}function Eb(a,b,c){function d(c){c&&b.appendChild(t(c)?a.createTextNode(c):c)}for(var e=2;e");f=f.join("")}f=d.createElement(f);g&&(t(g)?f.className=g:r(g)?f.className=g.join(" "):zb(f,g));2=a.keyCode)a.keyCode=-1}catch(b){}};var Rb="closure_lm_"+(1E6*Math.random()|0),Sb={},Tb=0;function S(a,b,c,d,e){if(r(b)){for(var f=0;fe.keyCode||void 0!=e.returnValue)){a:{var f=!1;if(0==e.keyCode)try{e.keyCode=-1;break a}catch(g){f=!0}if(f||void 0==e.returnValue)e.returnValue=!0}e=[];for(f=c.a;f;f=f.parentNode)e.push(f);for(var f=a.type,h=e.length-1;!c.f&&0<=h;h--){c.a=e[h];var k=$b(e[h],f,!0,c),d=d&&k}for(h=0;!c.f&&h>>0);function Ub(a){if("function"==q(a))return a;a[bc]||(a[bc]=function(b){return a.handleEvent(b)});return a[bc]};function cc(a){y.call(this);this.b=a;this.a={}}v(cc,y);var dc=[];cc.prototype.f=function(a,b,c,d){r(b)||(b&&(dc[0]=b.toString()),b=dc);for(var e=0;e=a||96<=a&&106>=a||65<=a&&90>=a||L&&0==a)return!0;switch(a){case 32:case 63:case 107:case 109:case 110:case 111:case 186:case 59:case 189:case 187:case 61:case 188:case 190:case 191:case 192:case 222:case 219:case 220:case 221:return!0;default:return!1}}function hc(a){if(K)a=ic(a);else if(M&&L)a:switch(a){case 93:a=91;break a}return a} +function ic(a){switch(a){case 61:return 187;case 59:return 186;case 173:return 189;case 224:return 91;case 0:return 224;default:return a}};function jc(a,b){U.call(this);a&&kc(this,a,b)}v(jc,U);l=jc.prototype;l.I=null;l.O=null;l.V=null;l.P=null;l.s=-1;l.w=-1;l.T=!1; +var lc={3:13,12:144,63232:38,63233:40,63234:37,63235:39,63236:112,63237:113,63238:114,63239:115,63240:116,63241:117,63242:118,63243:119,63244:120,63245:121,63246:122,63247:123,63248:44,63272:46,63273:36,63275:35,63276:33,63277:34,63289:144,63302:45},mc={Up:38,Down:40,Left:37,Right:39,Enter:13,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,"U+007F":46,Home:36,End:35,PageUp:33,PageDown:34,Insert:45},nc=J||L&&N("525"),oc=M&&K; +jc.prototype.a=function(a){L&&(17==this.s&&!a.ctrlKey||18==this.s&&!a.b||M&&91==this.s&&!a.metaKey)&&(this.w=this.s=-1);-1==this.s&&(a.ctrlKey&&17!=a.keyCode?this.s=17:a.b&&18!=a.keyCode?this.s=18:a.metaKey&&91!=a.keyCode&&(this.s=91));nc&&!fc(a.keyCode,this.s,a.l,a.ctrlKey,a.b)?this.handleEvent(a):(this.w=hc(a.keyCode),oc&&(this.T=a.b))};jc.prototype.b=function(a){this.w=this.s=-1;this.T=a.b}; +jc.prototype.handleEvent=function(a){var b=a.c,c,d,e=b.altKey;J&&"keypress"==a.type?(c=this.w,d=13!=c&&27!=c?b.keyCode:0):L&&"keypress"==a.type?(c=this.w,d=0<=b.charCode&&63232>b.charCode&&gc(c)?b.charCode:0):nb?(c=this.w,d=gc(c)?b.keyCode:0):(c=b.keyCode||this.w,d=b.charCode||0,oc&&(e=this.T),M&&63==d&&224==c&&(c=191));var f=c=hc(c),g=b.keyIdentifier;c?63232<=c&&c in lc?f=lc[c]:25==c&&a.l&&(f=9):g&&g in mc&&(f=mc[g]);this.s=f;a=new pc(f,d,0,b);a.b=e;V(this,a)}; +function kc(a,b,c){a.P&&qc(a);a.I=b;a.O=S(a.I,"keypress",a,c);a.V=S(a.I,"keydown",a.a,c,a);a.P=S(a.I,"keyup",a.b,c,a)}function qc(a){a.O&&(T(a.O),T(a.V),T(a.P),a.O=null,a.V=null,a.P=null);a.I=null;a.s=-1;a.w=-1}function pc(a,b,c,d){R.call(this,d);this.type="key";this.keyCode=a;this.i=b}v(pc,R);function W(a,b){var c=Q(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,null))?c[b]||c.getPropertyValue(b)||"":""}function X(a,b){return W(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style&&a.style[b]} +function rc(a){var b;try{b=a.getBoundingClientRect()}catch(c){return{left:0,top:0,right:0,bottom:0}}J&&a.ownerDocument.body&&(a=a.ownerDocument,b.left-=a.documentElement.clientLeft+a.body.clientLeft,b.top-=a.documentElement.clientTop+a.body.clientTop);return b} +function sc(a){if(J&&!O(8))return a.offsetParent;var b=Q(a),c=X(a,"position"),d="fixed"==c||"absolute"==c;for(a=a.parentNode;a&&a!=b;a=a.parentNode)if(11==a.nodeType&&a.host&&(a=a.host),c=X(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return null} +function tc(a){for(var b=new H(0,Infinity,Infinity,0),c=P(a),d=c.a.body,e=c.a.documentElement,f=Cb(c.a);a=sc(a);)if(!(J&&0==a.clientWidth||L&&0==a.clientHeight&&a==d)&&a!=d&&a!=e&&"visible"!=X(a,"overflow")){var g=Y(a),h=new G(a.clientLeft,a.clientTop);g.x+=h.x;g.y+=h.y;b.top=Math.max(b.top,g.y);b.right=Math.min(b.right,g.x+a.clientWidth);b.bottom=Math.min(b.bottom,g.y+a.clientHeight);b.left=Math.max(b.left,g.x)}d=f.scrollLeft;f=f.scrollTop;b.left=Math.max(b.left,d);b.top=Math.max(b.top,f);c=(Db(c.a)|| +window).document;c="CSS1Compat"==c.compatMode?c.documentElement:c.body;c=new A(c.clientWidth,c.clientHeight);b.right=Math.min(b.right,d+c.width);b.bottom=Math.min(b.bottom,f+c.height);return 0<=b.top&&0<=b.left&&b.bottom>b.top&&b.right>b.left?b:null}function Y(a){var b=Q(a),c=new G(0,0),d;d=b?Q(b):document;d=!J||O(9)||Hb(P(d))?d.documentElement:d.body;if(a==d)return c;a=rc(a);b=P(b);b=Bb(b.a);c.x=a.left+b.x;c.y=a.top+b.y;return c}function uc(a){"number"==typeof a&&(a=a+"px");return a} +function vc(a){var b=wc;if("none"!=X(a,"display"))return b(a);var c=a.style,d=c.display,e=c.visibility,f=c.position;c.visibility="hidden";c.position="absolute";c.display="inline";a=b(a);c.display=d;c.position=f;c.visibility=e;return a}function wc(a){var b=a.offsetWidth,c=a.offsetHeight,d=L&&!b&&!c;return(void 0===b||d)&&a.getBoundingClientRect?(a=rc(a),new A(a.right-a.left,a.bottom-a.top)):new A(b,c)}var xc=K?"MozUserSelect":L?"WebkitUserSelect":null; +function yc(a){var b=a.getElementsByTagName("*");if(xc){var c="none";a.style[xc]=c;if(b){a=0;for(var d;d=b[a];a++)d.style[xc]=c}}else if(J||nb)if(c="on",a.setAttribute("unselectable",c),b)for(a=0;d=b[a];a++)d.setAttribute("unselectable",c)}function zc(a,b){if(/^\d+px?$/.test(b))return parseInt(b,10);var c=a.style.left,d=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;a.style.left=b;var e=a.style.pixelLeft;a.style.left=c;a.runtimeStyle.left=d;return e} +function Ac(a,b){var c=a.currentStyle?a.currentStyle[b]:null;return c?zc(a,c):0}var Bc={thin:2,medium:4,thick:6};function Cc(a,b){if("none"==(a.currentStyle?a.currentStyle[b+"Style"]:null))return 0;var c=a.currentStyle?a.currentStyle[b+"Width"]:null;return c in Bc?Bc[c]:zc(a,c)} +function Dc(a){if(J&&!O(9)){var b=Cc(a,"borderLeft"),c=Cc(a,"borderRight"),d=Cc(a,"borderTop");a=Cc(a,"borderBottom");return new H(d,c,a,b)}b=W(a,"borderLeftWidth");c=W(a,"borderRightWidth");d=W(a,"borderTopWidth");a=W(a,"borderBottomWidth");return new H(parseFloat(d),parseFloat(c),parseFloat(a),parseFloat(b))};function Ec(a,b){return(b&4&&"rtl"==X(a,"direction")?b^2:b)&-5};function Fc(a,b){U.call(this);this.c=a||1;this.b=b||n;this.j=u(this.o,this);this.m=ha()}v(Fc,U);Fc.prototype.h=!1;Fc.prototype.a=null;Fc.prototype.o=function(){if(this.h){var a=ha()-this.m;0=a.a&&cc||c>=a.b.length?-1:c}l.update=function(a){this.J.update(a)};function Tc(a,b,c,d){U.call(this);this.v=a||document.body;this.j=P(this.v);this.qa=!a;this.b=null;this.D="";this.c=[];this.h=[];this.J=this.o=-1;this.l=!1;this.className="ac-renderer";this.oa="ac-highlighted";this.m=b||null;this.sa=null!=d?d:!0;this.ra=!!c}v(Tc,U);l=Tc.prototype;l.X=function(a,b,c){this.D=b;this.c=a;this.o=-1;this.J=ha();this.a=c;this.h=[];Uc(this)};l.u=function(){this.a&&Kb(this.a,null);this.l&&(this.l=!1,this.a&&Jb(this.a,"haspopup",!1),this.b.style.display="none")}; +function Vc(a){a.l||(a.l=!0,a.a&&(Ib(a.a,"combobox"),Jb(a.a,"autocomplete","list"),Jb(a.a,"haspopup",!0)),a.b.style.display="")} +function Wc(a,b){var c=0<=b&&b=b.right)&&(k&=-2),132==(k&132)&&(f.y=b.bottom)&&(k&=-5),f.xb.right&&(g.width=Math.min(b.right-f.x,h+g.width-b.left), +g.width=Math.max(g.width,0),m|=4)),f.x+g.width>b.right&&k&1&&(f.x=Math.max(b.right-g.width,b.left),m|=1),k&2&&(m=m|(f.xb.right?32:0)),f.yb.bottom&&(g.height=Math.min(b.bottom-f.y,h+g.height-b.top),g.height=Math.max(g.height,0),m|=8)),f.y+g.height>b.bottom&&k&4&&(f.y=Math.max(b.bottom-g.height,b.top),m|=2),k&8&&(m=m|(f.yb.bottom?128:0)),f=m):f=256;b=new kb(0,0,0, +0);b.left=c.x;b.top=c.y;b.width=e.width;b.height=e.height;f&496||(e=new G(b.left,b.top),e instanceof G?(c=e.x,e=e.y):(c=e,e=void 0),a.style.left=uc(c),a.style.top=uc(e),e=new A(b.width,b.height),d==e||d&&e&&d.width==e.width&&d.height==e.height||(d=e,c=Hb(P(Q(a))),!J||N("10")||c&&N("8")?(a=a.style,K?a.MozBoxSizing="border-box":L?a.WebkitBoxSizing="border-box":a.boxSizing="border-box",a.width=Math.max(d.width,0)+"px",a.height=Math.max(d.height,0)+"px"):(b=a.style,c?(J?(c=Ac(a,"paddingLeft"),e=Ac(a, +"paddingRight"),f=Ac(a,"paddingTop"),g=Ac(a,"paddingBottom"),c=new H(f,e,g,c)):(c=W(a,"paddingLeft"),e=W(a,"paddingRight"),f=W(a,"paddingTop"),g=W(a,"paddingBottom"),c=new H(parseFloat(f),parseFloat(e),parseFloat(g),parseFloat(c))),a=Dc(a),b.pixelWidth=d.width-a.left-c.left-c.right-a.right,b.pixelHeight=d.height-a.top-c.top-c.bottom-a.bottom):(b.pixelWidth=d.width,b.pixelHeight=d.height))))}} +function Yc(a,b,c){if(!a.K)if(3==b.nodeType){var d=null;r(c)&&1d;d++)e=2*d,b.nodeValue=c[e],f=a.j.a.createElement("B"),f.className=a.oa,f.appendChild(a.j.a.createTextNode(String(c[e+1]))),f=b.parentNode.insertBefore(f, +b.nextSibling),b.parentNode.insertBefore(a.j.a.createTextNode(""),f.nextSibling),b=f.nextSibling;b.nodeValue=Ka(c,2).join("");a.K=!0}else d&&Yc(a,b,d)}}else for(b=b.firstChild;b;)d=b.nextSibling,Yc(a,b,c),b=d}function $c(a){var b="";if(!a)return b;r(a)&&(a=Ga(a,function(a){return!/^[\s\xa0]*$/.test(null==a?"":String(a))}));r(a)?b=0ha()-this.J)&&V(this,{type:Ic,C:this.c[a].id})};var bd=E("iPhone")&&!E("iPod")&&!E("iPad")||E("iPod"),cd=E("iPad");!E("Android")||ib()||E("Firefox")||gb();ib();function dd(a,b,c,d){y.call(this);d=d||150;this.c=null!=c?c:!0;this.i=a||",;";this.v=this.i.substring(0,1);a=this.c?"[\\s"+this.i+"]+":"[\\s]+";this.o=new RegExp("^"+a+"|"+a+"$","g");this.S=new RegExp("\\s*["+this.i+"]$");this.m=b||"";this.D=this.c;this.f=0c.a)d--;else break;if(c.B(d))break a}b.preventDefault();return}break;case 9:if(!a.a.c.l||b.l)a.a.u();else if(a.update(),Nc(a.a)&&a.D){b.preventDefault();return}break;case 13:if(a.a.c.l){if(a.update(),Nc(a.a)){b.preventDefault();b.stopPropagation();return}}else a.a.u();break;case 27:if(a.a.c.l){a.a.u(); +b.preventDefault();b.stopPropagation();return}break;case 229:if(!a.A){a.A||(a.b.f(a.g,"keyup",a.da),a.b.f(a.g,"keypress",a.ca),a.A=!0);return}}hd(a,b)}function hd(a,b){var c=a.c&&b.i&&-1!=a.i.indexOf(String.fromCharCode(b.i));c&&a.update();c&&Nc(a.a)&&b.preventDefault()}l.ya=function(){return!1};l.$=function(a){fd(this,a.target||null)}; +function fd(a,b){var c=a.h;pa(c.a,T);c.a={};a.a&&Oc(a.a);b!=a.g&&(a.g=b,a.f&&(c=a.f,c.h=!0,c.a||(c.a=c.b.setTimeout(c.j,c.c),c.m=ha()),a.b.f(a.f,Gc,a.ga)),a.W=a.g.value,kc(a.l,a.g),a.b.f(a.l,"key",a.ea),a.b.f(a.g,"mousedown",a.fa),J&&a.b.f(a.g,"keypress",a.ba))}l.wa=function(){ed?window.setTimeout(u(this.ha,this),0):this.ha()}; +l.ha=function(){if(this.g){this.b.i(this.l,"key",this.ea);qc(this.l);this.b.i(this.g,"keyup",this.ya);this.b.i(this.g,"mousedown",this.fa);J&&this.b.i(this.g,"keypress",this.ba);this.A&&id(this);this.g=null;if(this.f){var a=this.f;a.h=!1;a.a&&(a.b.clearTimeout(a.a),a.a=null);this.b.i(this.f,Gc,this.ga)}this.a&&Pc(this.a)}};l.ga=function(){this.update()};l.Ca=function(a){this.$(a)};l.ea=function(a){this.j=a.keyCode;this.a&&gd(this,a)};l.ca=function(){this.A&&229!=this.j&&id(this)}; +l.da=function(a){this.A&&(13==a.keyCode||77==a.keyCode&&a.ctrlKey)&&id(this)};l.fa=function(){};function id(a){a.A&&(a.A=!1,a.b.i(a.g,"keypress",a.ca),a.b.i(a.g,"keyup",a.da))}l.ba=function(a){hd(this,a)}; +l.update=function(a){if(this.g&&(a||this.g.value!=this.W)){if(a||!this.Y){var b;a=Lb(this.g)[0];b=this.g.value;a=Sc(this,b)[Rc(this,b,a)];b=this.o?String(a).replace(this.o,""):a;if(this.a&&(this.a.o=this.g,a=this.a,a.m!=b)){a.m=b;var c=a.v;b=a.m;var d=a.D,e=u(a.Ba,a),c=c.a,f;f=[];if(""!=b)for(var g=Da(b),g=new RegExp("(^|\\W+)"+g,"i"),h=0;h td a"+location.hash);c&&a(c)}S(window,"hashchange",function(){C(b.querySelectorAll("tr.hilite"),function(a){Ya(a,"hilite")});var c=b.querySelector("tr > td a:target");c&&a(c)})}} +function ld(a){function b(){var a=c[e.value];a&&(window.location.href=Ta+a)}var c={},d=[];a.types&&C(a.types,function(a){qd(d,c,a)});a.modules&&C(a.modules,function(a){qd(d,c,a,!0)});a=document.querySelector("header form");S(a,"submit",function(a){a.preventDefault();a.stopPropagation();b();return!1});var e=a.querySelector("input");e.setAttribute("title","Search ("+(M?"⌘":"Ctrl+")+"E)");a=jd(d,e);a.D=20;S(a,"update",b);S(document.documentElement,"keydown",function(a){if(document.activeElement!==e&& +69===a.keyCode&&(M?a.metaKey:a.ctrlKey))return e.focus(),a.preventDefault(),a.stopPropagation(),!1;document.activeElement===e&&27===a.keyCode&&e.blur()})} +function qd(a,b,c,d,e){var f=c.name;e&&(f=e+(ta(e,")")?" ":".")+f);b[f]=c.href;a.push(f);d&&(f="("+f+")");d&&c.types&&C(c.types,function(c){qd(a,b,c,!1,f)});c.statics&&C(c.statics,function(d){var e=c.href+"#"+d;d=ta(f,")")?f+" "+d:-1===d.indexOf(".")?f+"."+d:f+d.slice(d.lastIndexOf("."));b[d]=e;a.push(d)});c.members&&C(c.members,function(d){b[f+"#"+d]=c.href+"#"+d;a.push(f+"#"+d)})} +function md(a){function b(a){window.localStorage&&window.localStorage.setItem(a.id,a.checked?"closed":"open")}var c="";Ta?c=window.location.pathname.split("/").slice(Ta.split("/").length).join("/"):window.location.pathname&&"/"!==window.location.pathname&&(c=window.location.pathname.slice(window.location.pathname.lastIndexOf("/")+1));var d=yb("nav-types-view");d&&a.types&&d.appendChild(Sa(a.types,c,!1));(d=yb("nav-modules-view"))&&a.modules&&d.appendChild(Sa(a.modules,c,!0));a=document.querySelector("nav"); +S(a,"keydown",function(a){if(37===a.keyCode||39===a.keyCode||32===a.keyCode){var c=Gb(a.target,function(a){return"LABEL"===a.tagName});c&&(c=document.getElementById(c.getAttribute("for")))&&(32===a.keyCode?(c.checked=!c.checked,a.preventDefault()):c.checked=37===a.keyCode,b(c))}});S(a,["focus","blur"],function(a){if(a.target.classList.contains("nav-item")&&"LABEL"===a.target.parentNode.tagName){var b=a.target.parentNode;"focus"===a.type?b.classList.add("focused"):b.classList.remove("focused")}},!0); +window.localStorage&&(C(a.querySelectorAll('input[type="checkbox"][id]'),function(a){var b=window.localStorage.getItem(a.id);a.checked=!t(b)||"closed"===b}),S(a,"change",function(a){b(a.target)}))};;init();})(); diff --git a/docs/enum_bot_ErrorCode.html b/docs/enum_bot_ErrorCode.html index 69098d9..c2f43de 100644 --- a/docs/enum_bot_ErrorCode.html +++ b/docs/enum_bot_ErrorCode.html @@ -1,2 +1,3 @@ -bot.ErrorCode \ No newline at end of file +ErrorCode

        enum ErrorCode

        Typenumber

        Error codes from the Selenium WebDriver protocol: +https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#response-status-codes

        +

        Values and Descriptions

        ELEMENT_NOT_SELECTABLE
        ELEMENT_NOT_VISIBLE
        IME_ENGINE_ACTIVATION_FAILED
        IME_NOT_AVAILABLE
        INVALID_ELEMENT_COORDINATES
        INVALID_ELEMENT_STATE
        INVALID_SELECTOR_ERROR
        INVALID_XPATH_SELECTOR
        INVALID_XPATH_SELECTOR_RETURN_TYPE
        JAVASCRIPT_ERROR
        METHOD_NOT_ALLOWED
        MOVE_TARGET_OUT_OF_BOUNDS
        NO_SUCH_ALERT
        NO_SUCH_ELEMENT
        NO_SUCH_FRAME
        NO_SUCH_WINDOW
        SCRIPT_TIMEOUT
        SESSION_NOT_CREATED
        SQL_DATABASE_ERROR
        STALE_ELEMENT_REFERENCE
        SUCCESS
        TIMEOUT
        UNEXPECTED_ALERT_OPEN
        UNKNOWN_COMMAND
        UNKNOWN_ERROR
        UNSUPPORTED_OPERATION
        XPATH_LOOKUP_ERROR
        \ No newline at end of file diff --git a/docs/enum_bot_Error_State.html b/docs/enum_bot_Error_State.html index 49054c1..9038563 100644 --- a/docs/enum_bot_Error_State.html +++ b/docs/enum_bot_Error_State.html @@ -1 +1,2 @@ -bot.Error.State \ No newline at end of file +Error.State

        enum Error.State

        Typestring

        Status strings enumerated in the W3C WebDriver protocol.

        +

        Values and Descriptions

        ELEMENT_NOT_SELECTABLE
        ELEMENT_NOT_VISIBLE
        INVALID_ARGUMENT
        INVALID_ELEMENT_COORDINATES
        INVALID_ELEMENT_STATE
        INVALID_SELECTOR
        INVALID_SESSION_ID
        JAVASCRIPT_ERROR
        MOVE_TARGET_OUT_OF_BOUNDS
        NO_SUCH_ALERT
        NO_SUCH_ELEMENT
        NO_SUCH_FRAME
        NO_SUCH_WINDOW
        SCRIPT_TIMEOUT
        SESSION_NOT_CREATED
        STALE_ELEMENT_REFERENCE
        TIMEOUT
        UNEXPECTED_ALERT_OPEN
        UNKNOWN_COMMAND
        UNKNOWN_ERROR
        UNKNOWN_METHOD
        UNSUPPORTED_OPERATION
        \ No newline at end of file diff --git a/docs/enum_goog_Disposable_MonitoringMode.html b/docs/enum_goog_Disposable_MonitoringMode.html new file mode 100644 index 0000000..14cbf04 --- /dev/null +++ b/docs/enum_goog_Disposable_MonitoringMode.html @@ -0,0 +1,6 @@ +goog.Disposable.MonitoringMode

        Enum goog.Disposable.MonitoringMode

        code »
        Type: number

        Values and Descriptions

        INTERACTIVE
        INTERACTIVE mode can be switched on and off on the fly without producing + errors. It also doesn't warn if the disposable objects don't call the + goog.Disposable base constructor.
        OFF
        No monitoring.
        PERMANENT
        Creating and disposing the goog.Disposable instances is monitored. All + disposable objects need to call the goog.Disposable base + constructor. The PERMANENT mode must be switched on before creating any + goog.Disposable instances.
        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_dom_BrowserFeature.html b/docs/enum_goog_dom_BrowserFeature.html new file mode 100644 index 0000000..9aa63db --- /dev/null +++ b/docs/enum_goog_dom_BrowserFeature.html @@ -0,0 +1,8 @@ +goog.dom.BrowserFeature

        Enum goog.dom.BrowserFeature

        code »
        Type: boolean

        Enum of browser capabilities.

        Values and Descriptions

        CAN_ADD_NAME_OR_TYPE_ATTRIBUTES
        Whether attributes 'name' and 'type' can be added to an element after it's + created. False in Internet Explorer prior to version 9.
        CAN_USE_CHILDREN_ATTRIBUTE
        Whether we can use element.children to access an element's Element + children. Available since Gecko 1.9.1, IE 9. (IE<9 also includes comment + nodes in the collection.)
        CAN_USE_INNER_TEXT
        Opera, Safari 3, and Internet Explorer 9 all support innerText but they + include text nodes in script and style tags. Not document-mode-dependent.
        CAN_USE_PARENT_ELEMENT_PROPERTY
        MSIE, Opera, and Safari>=4 support element.parentElement to access an + element's parent if it is an Element.
        INNER_HTML_NEEDS_SCOPED_ELEMENT
        Whether NoScope elements need a scoped element written before them in + innerHTML. + MSDN: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx#1
        LEGACY_IE_RANGES
        Whether we use legacy IE range API.
        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_dom_NodeType.html b/docs/enum_goog_dom_NodeType.html new file mode 100644 index 0000000..d906098 --- /dev/null +++ b/docs/enum_goog_dom_NodeType.html @@ -0,0 +1,10 @@ +goog.dom.NodeType

        Enum goog.dom.NodeType

        code »
        Type: number

        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

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_dom_TagName.html b/docs/enum_goog_dom_TagName.html new file mode 100644 index 0000000..7e46c2d --- /dev/null +++ b/docs/enum_goog_dom_TagName.html @@ -0,0 +1,2 @@ +goog.dom.TagName \ No newline at end of file diff --git a/docs/enum_goog_events_BrowserEvent_MouseButton.html b/docs/enum_goog_events_BrowserEvent_MouseButton.html new file mode 100644 index 0000000..01bc09b --- /dev/null +++ b/docs/enum_goog_events_BrowserEvent_MouseButton.html @@ -0,0 +1 @@ +goog.events.BrowserEvent.MouseButton

        Enum goog.events.BrowserEvent.MouseButton

        code »
        Type: number

        Normalized button constants for the mouse.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_events_BrowserFeature.html b/docs/enum_goog_events_BrowserFeature.html new file mode 100644 index 0000000..3bb8a01 --- /dev/null +++ b/docs/enum_goog_events_BrowserFeature.html @@ -0,0 +1,4 @@ +goog.events.BrowserFeature

        Enum goog.events.BrowserFeature

        code »
        Type: boolean

        Enum of browser capabilities.

        Values and Descriptions

        HAS_HTML5_NETWORK_EVENT_SUPPORT
        Whether HTML5 network online/offline events are supported.
        HAS_NAVIGATOR_ONLINE_PROPERTY
        Whether the navigator.onLine property is supported.
        HAS_W3C_BUTTON
        Whether the button attribute of the event is W3C compliant. False in + Internet Explorer prior to version 9; document-version dependent.
        HAS_W3C_EVENT_SUPPORT
        Whether the browser supports full W3C event model.
        HTML5_NETWORK_EVENTS_FIRE_ON_BODY
        Whether HTML5 network events fire on document.body, or otherwise the + window.
        SET_KEY_CODE_TO_PREVENT_DEFAULT
        To prevent default in IE7-8 for certain keydown events we need set the + keyCode to -1.
        TOUCH_ENABLED
        Whether touch is enabled in the browser.
        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_events_CaptureSimulationMode.html b/docs/enum_goog_events_CaptureSimulationMode.html new file mode 100644 index 0000000..81b2ea6 --- /dev/null +++ b/docs/enum_goog_events_CaptureSimulationMode.html @@ -0,0 +1,3 @@ +goog.events.CaptureSimulationMode

        Enum goog.events.CaptureSimulationMode

        code »
        Type: number

        Values and Descriptions

        OFF_AND_FAIL
        Does not perform capture simulation. Will asserts in IE8- when you + add capture listeners.
        OFF_AND_SILENT
        Does not perform capture simulation, silently ignore capture + listeners.
        ON
        Performs capture simulation.
        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_events_EventType.html b/docs/enum_goog_events_EventType.html new file mode 100644 index 0000000..0315e7f --- /dev/null +++ b/docs/enum_goog_events_EventType.html @@ -0,0 +1 @@ +goog.events.EventType \ No newline at end of file diff --git a/docs/enum_goog_events_KeyCodes.html b/docs/enum_goog_events_KeyCodes.html new file mode 100644 index 0000000..ffa4f95 --- /dev/null +++ b/docs/enum_goog_events_KeyCodes.html @@ -0,0 +1,22 @@ +goog.events.KeyCodes

        Enum goog.events.KeyCodes

        code »
        Type: number

        Key codes for common characters. + + This list is not localized and therefore some of the key codes are not + correct for non US keyboard layouts. See comments below.

        Values and Descriptions

        Show:

        Global Functions

        code »goog.events.KeyCodes.firesKeyPressEvent ( keyCode, opt_heldKeyCode, opt_shiftKey, opt_ctrlKey, opt_altKey )boolean

        Returns true if the key fires a keypress event in the current browser. + + Accoridng to MSDN [1] IE only fires keypress events for the following keys: + - Letters: A - Z (uppercase and lowercase) + - Numerals: 0 - 9 + - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~ + - System: ESC, SPACEBAR, ENTER + + That's not entirely correct though, for instance there's no distinction + between upper and lower case letters. + + [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx) + + Safari is similar to IE, but does not fire keypress for ESC. + + Additionally, IE6 does not fire keydown or keypress events for letters when + the control or alt keys are held down and the shift key is not. IE7 does + fire keydown in these cases, though, but not keypress.

        Parameters
        keyCode: number
        A key code.
        opt_heldKeyCode: number=
        Key code of a currently-held key.
        opt_shiftKey: boolean=
        Whether the shift key is held down.
        opt_ctrlKey: boolean=
        Whether the control key is held down.
        opt_altKey: boolean=
        Whether the alt key is held down.
        Returns
        Whether it's a key that fires a keypress event.

        Returns true if the key produces a character. + This does not cover characters on non-US keyboards (Russian, Hebrew, etc.).

        Parameters
        keyCode: number
        A key code.
        Returns
        Whether it's a character key.

        Returns true if the event contains a text modifying key.

        Parameters
        e: goog.events.BrowserEvent
        A key event.
        Returns
        Whether it's a text modifying key.

        Normalizes key codes from their Gecko-specific value to the general one.

        Parameters
        keyCode: number
        The native key code.
        Returns
        The normalized key code.

        Normalizes key codes from OS/Browser-specific value to the general one.

        Parameters
        keyCode: number
        The native key code.
        Returns
        The normalized key code.

        Normalizes key codes from their Mac WebKit-specific value to the general one.

        Parameters
        keyCode: number
        The native key code.
        Returns
        The normalized key code.
        \ No newline at end of file diff --git a/docs/enum_goog_net_XmlHttp_OptionType.html b/docs/enum_goog_net_XmlHttp_OptionType.html index d16e1ee..32b8726 100644 --- a/docs/enum_goog_net_XmlHttp_OptionType.html +++ b/docs/enum_goog_net_XmlHttp_OptionType.html @@ -1,4 +1,4 @@ -goog.net.XmlHttp.OptionType

        Enum goog.net.XmlHttp.OptionType

        code »
        Type: number

        Type of options that an XmlHttp object can have.

        Values and Descriptions

        LOCAL_REQUEST_ERROR
        NOTE(user): In IE if send() errors on a *local* request the readystate +goog.net.XmlHttp.OptionType

        Enum goog.net.XmlHttp.OptionType

        code »
        Type: number

        Type of options that an XmlHttp object can have.

        Values and Descriptions

        LOCAL_REQUEST_ERROR
        NOTE(user): In IE if send() errors on a *local* request the readystate is still changed to COMPLETE. We need to ignore it and allow the try/catch around send() to pick up the error.
        USE_NULL_FUNCTION
        Whether a goog.nullFunction should be used to clear the onreadystatechange - handler instead of null.
        Show:
        \ No newline at end of file + handler instead of null.
        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_net_XmlHttp_ReadyState.html b/docs/enum_goog_net_XmlHttp_ReadyState.html index 41635c9..7c4404e 100644 --- a/docs/enum_goog_net_XmlHttp_ReadyState.html +++ b/docs/enum_goog_net_XmlHttp_ReadyState.html @@ -1,3 +1,3 @@ -goog.net.XmlHttp.ReadyState

        Enum goog.net.XmlHttp.ReadyState

        code »
        Type: number

        Status constants for XMLHTTP, matches: +goog.net.XmlHttp.ReadyState

        Enum goog.net.XmlHttp.ReadyState

        code »
        Type: number

        Status constants for XMLHTTP, matches: http://msdn.microsoft.com/library/default.asp?url=/library/ - en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp

        Values and Descriptions

        COMPLETE
        Constant for when xmlhttprequest.readyState is completed
        INTERACTIVE
        Constant for when xmlhttprequest.readyState is in an interactive state.
        LOADED
        Constant for when xmlhttprequest.readyState is loaded.
        LOADING
        Constant for when xmlhttprequest.readyState is loading.
        UNINITIALIZED
        Constant for when xmlhttprequest.readyState is uninitialized
        Show:
        \ No newline at end of file + en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp

        Values and Descriptions

        COMPLETE
        Constant for when xmlhttprequest.readyState is completed
        INTERACTIVE
        Constant for when xmlhttprequest.readyState is in an interactive state.
        LOADED
        Constant for when xmlhttprequest.readyState is loaded.
        LOADING
        Constant for when xmlhttprequest.readyState is loading.
        UNINITIALIZED
        Constant for when xmlhttprequest.readyState is uninitialized
        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_string_Unicode.html b/docs/enum_goog_string_Unicode.html index 9dd3b43..e0c1683 100644 --- a/docs/enum_goog_string_Unicode.html +++ b/docs/enum_goog_string_Unicode.html @@ -1 +1 @@ -goog.string.Unicode

        Enum goog.string.Unicode

        code »
        Type: string

        Common Unicode string characters.

        Values and Descriptions

        Show:
        \ No newline at end of file +goog.string.Unicode

        Enum goog.string.Unicode

        code »
        Type: string

        Common Unicode string characters.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_testing_TestCase_Order.html b/docs/enum_goog_testing_TestCase_Order.html new file mode 100644 index 0000000..efa7872 --- /dev/null +++ b/docs/enum_goog_testing_TestCase_Order.html @@ -0,0 +1,2 @@ +goog.testing.TestCase.Order

        Enum goog.testing.TestCase.Order

        code »
        Type: string

        The order to run the auto-discovered tests.

        Values and Descriptions

        NATURAL
        This is browser dependent and known to be different in FF and Safari + compared to others.
        RANDOM
        Random order.
        SORTED
        Sorted based on the name.
        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_uri_utils_CharCode_.html b/docs/enum_goog_uri_utils_CharCode_.html index 4c9fb9e..473287a 100644 --- a/docs/enum_goog_uri_utils_CharCode_.html +++ b/docs/enum_goog_uri_utils_CharCode_.html @@ -1 +1 @@ -goog.uri.utils.CharCode_

        Enum goog.uri.utils.CharCode_

        code »
        Type: number

        Character codes inlined to avoid object allocations due to charCode.

        Values and Descriptions

        Show:
        \ No newline at end of file +goog.uri.utils.CharCode_

        Enum goog.uri.utils.CharCode_

        code »
        Type: number

        Character codes inlined to avoid object allocations due to charCode.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_uri_utils_ComponentIndex.html b/docs/enum_goog_uri_utils_ComponentIndex.html index 82afbbb..61588fe 100644 --- a/docs/enum_goog_uri_utils_ComponentIndex.html +++ b/docs/enum_goog_uri_utils_ComponentIndex.html @@ -1 +1 @@ -goog.uri.utils.ComponentIndex

        Enum goog.uri.utils.ComponentIndex

        code »
        Type: number

        The index of each URI component in the return value of goog.uri.utils.split.

        Values and Descriptions

        Show:
        \ No newline at end of file +goog.uri.utils.ComponentIndex

        Enum goog.uri.utils.ComponentIndex

        code »
        Type: number

        The index of each URI component in the return value of goog.uri.utils.split.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_goog_uri_utils_StandardQueryParam.html b/docs/enum_goog_uri_utils_StandardQueryParam.html index 255b326..2ae6cf5 100644 --- a/docs/enum_goog_uri_utils_StandardQueryParam.html +++ b/docs/enum_goog_uri_utils_StandardQueryParam.html @@ -1 +1 @@ -goog.uri.utils.StandardQueryParam

        Enum goog.uri.utils.StandardQueryParam

        code »
        Type: string

        Standard supported query parameters.

        Values and Descriptions

        RANDOM
        Unused parameter for unique-ifying.
        Show:
        \ No newline at end of file +goog.uri.utils.StandardQueryParam

        Enum goog.uri.utils.StandardQueryParam

        code »
        Type: string

        Standard supported query parameters.

        Values and Descriptions

        RANDOM
        Unused parameter for unique-ifying.
        Show:
        \ No newline at end of file diff --git a/docs/enum_webdriver_Browser.html b/docs/enum_webdriver_Browser.html index a50d566..2b55b21 100644 --- a/docs/enum_webdriver_Browser.html +++ b/docs/enum_webdriver_Browser.html @@ -1 +1,2 @@ -webdriver.Browser

        Enum webdriver.Browser

        code »
        Type: string

        Recognized browser names.

        Values and Descriptions

        Show:
        \ No newline at end of file +Browser

        enum Browser

        Typestring

        Recognized browser names.

        +

        Values and Descriptions

        ANDROID
        CHROME
        FIREFOX
        HTMLUNIT
        IE
        INTERNET_EXPLORER
        IPAD
        IPHONE
        OPERA
        PHANTOM_JS
        SAFARI
        \ No newline at end of file diff --git a/docs/enum_webdriver_Button.html b/docs/enum_webdriver_Button.html index 7b2018b..492d103 100644 --- a/docs/enum_webdriver_Button.html +++ b/docs/enum_webdriver_Button.html @@ -1 +1,2 @@ -webdriver.Button

        Enum webdriver.Button

        code »
        Type: number

        Enumeration of the buttons used in the advanced interactions API.

        Values and Descriptions

        Show:
        \ No newline at end of file +Button

        enum Button

        Typenumber

        Enumeration of the buttons used in the advanced interactions API.

        +

        Values and Descriptions

        LEFT
        MIDDLE
        \ No newline at end of file diff --git a/docs/enum_webdriver_Capability.html b/docs/enum_webdriver_Capability.html index a16afd9..6a15c11 100644 --- a/docs/enum_webdriver_Capability.html +++ b/docs/enum_webdriver_Capability.html @@ -1,14 +1,33 @@ -webdriver.Capability

        Enum webdriver.Capability

        code »
        Type: string

        Common webdriver capability keys.

        Values and Descriptions

        ACCEPT_SSL_CERTS
        Indicates whether a driver should accept all SSL certs by default. This - capability only applies when requesting a new session. To query whether - a driver can handle insecure SSL certs, see - webdriver.Capability.SECURE_SSL.
        BROWSER_NAME
        The browser name. Common browser names are defined in the - webdriver.Browser enum.
        HANDLES_ALERTS
        Whether the driver is capable of handling modal alerts (e.g. alert, - confirm, prompt). To define how a driver should handle alerts, - use webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR.
        LOGGING_PREFS
        Key for the logging driver logging preferences.
        PLATFORM
        Describes the platform the browser is running on. Will be one of - ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When requesting a - session, ANY may be used to indicate no platform preference (this is - semantically equivalent to omitting the platform capability).
        PROXY
        Describes the proxy configuration to use for a new WebDriver session.
        ROTATABLE
        Whether the driver supports changing the brower's orientation.
        SECURE_SSL
        Whether a driver is only capable of handling secure SSL certs. To request - that a driver accept insecure SSL certs by default, use - webdriver.Capability.ACCEPT_SSL_CERTS.
        SUPPORTS_APPLICATION_CACHE
        Whether the driver supports manipulating the app cache.
        SUPPORTS_BROWSER_CONNECTION
        Whether the driver supports controlling the browser's internet - connectivity.
        SUPPORTS_CSS_SELECTORS
        Whether the driver supports locating elements with CSS selectors.
        SUPPORTS_JAVASCRIPT
        Whether the browser supports JavaScript.
        SUPPORTS_LOCATION_CONTEXT
        Whether the driver supports controlling the browser's location info.
        TAKES_SCREENSHOT
        Whether the driver supports taking screenshots.
        UNEXPECTED_ALERT_BEHAVIOR
        Defines how the driver should handle unexpected alerts. The value should - be one of "accept", "dismiss", or "ignore.
        VERSION
        Defines the browser version.
        Show:
        \ No newline at end of file +Capability

        enum Capability

        Typestring

        Common webdriver capability keys.

        +

        Values and Descriptions

        ACCEPT_SSL_CERTS

        Indicates whether a driver should accept all SSL certs by default. This +capability only applies when requesting a new session. To query whether +a driver can handle insecure SSL certs, see #SECURE_SSL.

        +
        BROWSER_NAME

        The browser name. Common browser names are defined in the +webdriver.Browser enum.

        +
        ELEMENT_SCROLL_BEHAVIOR

        Defines how elements should be scrolled into the viewport for interaction. +This capability will be set to zero (0) if elements are aligned with the +top of the viewport, or one (1) if aligned with the bottom. The default +behavior is to align with the top of the viewport.

        +
        HANDLES_ALERTS

        Whether the driver is capable of handling modal alerts (e.g. alert, +confirm, prompt). To define how a driver should handle alerts, +use #UNEXPECTED_ALERT_BEHAVIOR.

        +
        LOGGING_PREFS

        Key for the logging driver logging preferences.

        +
        NATIVE_EVENTS

        Whether this session generates native events when simulating user input.

        +
        PLATFORM

        Describes the platform the browser is running on. Will be one of +ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When requesting a +session, ANY may be used to indicate no platform preference (this is +semantically equivalent to omitting the platform capability).

        +
        PROXY

        Describes the proxy configuration to use for a new WebDriver session.

        +
        ROTATABLE

        Whether the driver supports changing the brower's orientation.

        +
        SECURE_SSL

        Whether a driver is only capable of handling secure SSL certs. To request +that a driver accept insecure SSL certs by default, use +#ACCEPT_SSL_CERTS.

        +
        SUPPORTS_APPLICATION_CACHE

        Whether the driver supports manipulating the app cache.

        +
        SUPPORTS_CSS_SELECTORS

        Whether the driver supports locating elements with CSS selectors.

        +
        SUPPORTS_JAVASCRIPT

        Whether the browser supports JavaScript.

        +
        SUPPORTS_LOCATION_CONTEXT

        Whether the driver supports controlling the browser's location info.

        +
        TAKES_SCREENSHOT

        Whether the driver supports taking screenshots.

        +
        UNEXPECTED_ALERT_BEHAVIOR

        Defines how the driver should handle unexpected alerts. The value should +be one of "accept", "dismiss", or "ignore.

        +
        VERSION

        Defines the browser version.

        +
        \ No newline at end of file diff --git a/docs/enum_webdriver_CommandName.html b/docs/enum_webdriver_CommandName.html index 51e2c1b..8cc80ea 100644 --- a/docs/enum_webdriver_CommandName.html +++ b/docs/enum_webdriver_CommandName.html @@ -1,2 +1,3 @@ -webdriver.CommandName

        Enum webdriver.CommandName

        code »
        Type: string

        Enumeration of predefined names command names that all command processors - will support.

        Values and Descriptions

        ACCEPT_ALERT
        ADD_COOKIE
        CLEAR_APP_CACHE
        CLEAR_ELEMENT
        CLEAR_LOCAL_STORAGE
        CLEAR_SESSION_STORAGE
        CLICK
        CLICK_ELEMENT
        CLOSE
        DELETE_ALL_COOKIES
        DELETE_COOKIE
        DESCRIBE_SESSION
        DISMISS_ALERT
        DOUBLE_CLICK
        ELEMENT_EQUALS
        EXECUTE_ASYNC_SCRIPT
        EXECUTE_SCRIPT
        EXECUTE_SQL
        FIND_CHILD_ELEMENT
        FIND_CHILD_ELEMENTS
        FIND_ELEMENT
        FIND_ELEMENTS
        GET
        GET_ACTIVE_ELEMENT
        GET_ALERT_TEXT
        GET_ALL_COOKIES
        GET_APP_CACHE
        GET_APP_CACHE_STATUS
        GET_AVAILABLE_LOG_TYPES
        GET_COOKIE
        GET_CURRENT_URL
        GET_CURRENT_WINDOW_HANDLE
        GET_ELEMENT_ATTRIBUTE
        GET_ELEMENT_LOCATION
        GET_ELEMENT_LOCATION_IN_VIEW
        GET_ELEMENT_SIZE
        GET_ELEMENT_TAG_NAME
        GET_ELEMENT_TEXT
        GET_ELEMENT_VALUE_OF_CSS_PROPERTY
        GET_LOCAL_STORAGE_ITEM
        GET_LOCAL_STORAGE_KEYS
        GET_LOCAL_STORAGE_SIZE
        GET_LOCATION
        GET_LOG
        GET_PAGE_SOURCE
        GET_SCREEN_ORIENTATION
        GET_SERVER_STATUS
        GET_SESSIONS
        GET_SESSION_LOGS
        GET_SESSION_STORAGE_ITEM
        GET_SESSION_STORAGE_KEYS
        GET_SESSION_STORAGE_SIZE
        GET_TITLE
        GET_WINDOW_HANDLES
        GET_WINDOW_POSITION
        GET_WINDOW_SIZE
        GO_BACK
        GO_FORWARD
        IMPLICITLY_WAIT
        IS_BROWSER_ONLINE
        IS_ELEMENT_DISPLAYED
        IS_ELEMENT_ENABLED
        IS_ELEMENT_SELECTED
        MAXIMIZE_WINDOW
        MOUSE_DOWN
        MOUSE_UP
        MOVE_TO
        NEW_SESSION
        QUIT
        REFRESH
        REMOVE_LOCAL_STORAGE_ITEM
        REMOVE_SESSION_STORAGE_ITEM
        SCREENSHOT
        SEND_KEYS_TO_ACTIVE_ELEMENT
        SEND_KEYS_TO_ELEMENT
        SET_ALERT_TEXT
        SET_BROWSER_ONLINE
        SET_LOCAL_STORAGE_ITEM
        SET_LOCATION
        SET_SCREEN_ORIENTATION
        SET_SCRIPT_TIMEOUT
        SET_SESSION_STORAGE_ITEM
        SET_TIMEOUT
        SET_WINDOW_POSITION
        SET_WINDOW_SIZE
        SUBMIT_ELEMENT
        SWITCH_TO_FRAME
        SWITCH_TO_WINDOW
        TOUCH_DOUBLE_TAP
        TOUCH_DOWN
        TOUCH_FLICK
        TOUCH_LONG_PRESS
        TOUCH_MOVE
        TOUCH_SCROLL
        TOUCH_SINGLE_TAP
        TOUCH_UP
        Show:
        \ No newline at end of file +CommandName

        enum CommandName

        Typestring

        Enumeration of predefined names command names that all command processors +will support.

        +

        Values and Descriptions

        ACCEPT_ALERT
        CLEAR_APP_CACHE
        CLEAR_ELEMENT
        CLEAR_LOCAL_STORAGE
        CLEAR_SESSION_STORAGE
        CLICK
        CLICK_ELEMENT
        CLOSE
        DELETE_ALL_COOKIES
        DESCRIBE_SESSION
        DISMISS_ALERT
        DOUBLE_CLICK
        ELEMENT_EQUALS
        EXECUTE_ASYNC_SCRIPT
        EXECUTE_SCRIPT
        EXECUTE_SQL
        FIND_CHILD_ELEMENT
        FIND_CHILD_ELEMENTS
        FIND_ELEMENT
        FIND_ELEMENTS
        GET
        GET_ACTIVE_ELEMENT
        GET_ALERT_TEXT
        GET_ALL_COOKIES
        GET_APP_CACHE
        GET_APP_CACHE_STATUS
        GET_AVAILABLE_LOG_TYPES
        GET_CURRENT_URL
        GET_CURRENT_WINDOW_HANDLE
        GET_ELEMENT_ATTRIBUTE
        GET_ELEMENT_LOCATION
        GET_ELEMENT_LOCATION_IN_VIEW
        GET_ELEMENT_SIZE
        GET_ELEMENT_TAG_NAME
        GET_ELEMENT_TEXT
        GET_ELEMENT_VALUE_OF_CSS_PROPERTY
        GET_LOCAL_STORAGE_ITEM
        GET_LOCAL_STORAGE_KEYS
        GET_LOCAL_STORAGE_SIZE
        GET_LOCATION
        GET_LOG
        GET_PAGE_SOURCE
        GET_SCREEN_ORIENTATION
        GET_SERVER_STATUS
        GET_SESSIONS
        GET_SESSION_LOGS
        GET_SESSION_STORAGE_ITEM
        GET_SESSION_STORAGE_KEYS
        GET_SESSION_STORAGE_SIZE
        GET_TITLE
        GET_WINDOW_HANDLES
        GET_WINDOW_POSITION
        GET_WINDOW_SIZE
        GO_BACK
        GO_FORWARD
        IMPLICITLY_WAIT
        IS_BROWSER_ONLINE
        IS_ELEMENT_DISPLAYED
        IS_ELEMENT_ENABLED
        IS_ELEMENT_SELECTED
        MAXIMIZE_WINDOW
        MOUSE_DOWN
        MOUSE_UP
        MOVE_TO
        NEW_SESSION
        QUIT
        REFRESH
        REMOVE_LOCAL_STORAGE_ITEM
        REMOVE_SESSION_STORAGE_ITEM
        SCREENSHOT
        SEND_KEYS_TO_ACTIVE_ELEMENT
        SEND_KEYS_TO_ELEMENT
        SET_ALERT_TEXT
        SET_BROWSER_ONLINE
        SET_LOCAL_STORAGE_ITEM
        SET_LOCATION
        SET_SCREEN_ORIENTATION
        SET_SCRIPT_TIMEOUT
        SET_SESSION_STORAGE_ITEM
        SET_TIMEOUT
        SET_WINDOW_POSITION
        SET_WINDOW_SIZE
        SUBMIT_ELEMENT
        SWITCH_TO_FRAME
        SWITCH_TO_WINDOW
        TOUCH_DOUBLE_TAP
        TOUCH_DOWN
        TOUCH_FLICK
        TOUCH_LONG_PRESS
        TOUCH_MOVE
        TOUCH_SCROLL
        TOUCH_SINGLE_TAP
        TOUCH_UP
        UPLOAD_FILE
        \ No newline at end of file diff --git a/docs/enum_webdriver_FirefoxDomExecutor_Attribute_.html b/docs/enum_webdriver_FirefoxDomExecutor_Attribute_.html index 991d820..e8dddad 100644 --- a/docs/enum_webdriver_FirefoxDomExecutor_Attribute_.html +++ b/docs/enum_webdriver_FirefoxDomExecutor_Attribute_.html @@ -1 +1 @@ -webdriver.FirefoxDomExecutor.Attribute_

        Enum webdriver.FirefoxDomExecutor.Attribute_

        code »
        Type: string

        Attributes used to communicate with the FirefoxDriver extension.

        Values and Descriptions

        Show:
        \ No newline at end of file +webdriver.FirefoxDomExecutor.Attribute_

        Enum webdriver.FirefoxDomExecutor.Attribute_

        code »
        Type: string

        Attributes used to communicate with the FirefoxDriver extension.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_webdriver_FirefoxDomExecutor_EventType_.html b/docs/enum_webdriver_FirefoxDomExecutor_EventType_.html index 77449eb..f59505e 100644 --- a/docs/enum_webdriver_FirefoxDomExecutor_EventType_.html +++ b/docs/enum_webdriver_FirefoxDomExecutor_EventType_.html @@ -1 +1 @@ -webdriver.FirefoxDomExecutor.EventType_

        Enum webdriver.FirefoxDomExecutor.EventType_

        code »
        Type: string

        Events used to communicate with the FirefoxDriver extension.

        Values and Descriptions

        Show:
        \ No newline at end of file +webdriver.FirefoxDomExecutor.EventType_

        Enum webdriver.FirefoxDomExecutor.EventType_

        code »
        Type: string

        Events used to communicate with the FirefoxDriver extension.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_webdriver_Key.html b/docs/enum_webdriver_Key.html index 04cb3f2..f10c503 100644 --- a/docs/enum_webdriver_Key.html +++ b/docs/enum_webdriver_Key.html @@ -1,9 +1,12 @@ -webdriver.Key

        Enum webdriver.Key

        code »
        Type: string

        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

        Values and Descriptions

        Show:

        Global Functions

        Simulate pressing many keys at once in a "chord". Takes a sequence of - webdriver.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
        var_args: ...string
        The key sequence to concatenate.
        Returns
        The null-terminated key sequence.
        \ No newline at end of file +Key

        enum Key

        Typestring

        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

        +

        Values and Descriptions

        ADD
        ALT
        ARROW_DOWN
        ARROW_LEFT
        ARROW_RIGHT
        ARROW_UP
        BACK_SPACE
        CANCEL
        CLEAR
        COMMAND
        CONTROL
        DECIMAL
        DELETE
        DIVIDE
        DOWN
        END
        ENTER
        EQUALS
        ESCAPE
        F1
        F10
        F11
        F12
        F2
        F3
        F4
        F5
        F6
        F7
        F8
        F9
        HELP
        HOME
        INSERT
        LEFT
        META
        MULTIPLY
        NULL
        NUMPAD0
        NUMPAD1
        NUMPAD2
        NUMPAD3
        NUMPAD4
        NUMPAD5
        NUMPAD6
        NUMPAD7
        NUMPAD8
        NUMPAD9
        PAGE_DOWN
        PAGE_UP
        PAUSE
        RETURN
        SEMICOLON
        SEPARATOR
        SHIFT
        SPACE
        SUBTRACT
        TAB
        UP

        Functions

        Key.chord(var_args)code »

        Simulate pressing many keys at once in a "chord". Takes a sequence of +webdriver.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
        var_args...string

        The key sequence to concatenate.

        +
        Returns
        string

        The null-terminated key sequence.

        +
        \ No newline at end of file diff --git a/docs/enum_webdriver_logging_Level.html b/docs/enum_webdriver_logging_Level.html index 96cce56..be4d555 100644 --- a/docs/enum_webdriver_logging_Level.html +++ b/docs/enum_webdriver_logging_Level.html @@ -1 +1 @@ -webdriver.logging.Level

        Enum webdriver.logging.Level

        code »
        Type: {value: number, name: webdriver.logging.LevelName}

        Logging levels.

        Values and Descriptions

        Show:
        \ No newline at end of file +webdriver.logging.Level

        Enum webdriver.logging.Level

        code »
        Type: {value: number, name: string}

        Logging levels.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/enum_webdriver_logging_Type.html b/docs/enum_webdriver_logging_Type.html index c69f777..af2f036 100644 --- a/docs/enum_webdriver_logging_Type.html +++ b/docs/enum_webdriver_logging_Type.html @@ -1 +1,6 @@ -webdriver.logging.Type

        Enum webdriver.logging.Type

        code »
        Type: string

        Common log types.

        Values and Descriptions

        BROWSER
        Logs originating from the browser.
        CLIENT
        Logs from a WebDriver client.
        DRIVER
        Logs from a WebDriver implementation.
        PERFORMANCE
        Logs related to performance.
        SERVER
        Logs from the remote server.
        Show:
        \ No newline at end of file +Type

        enum Type

        Typestring

        Values and Descriptions

        BROWSER

        Logs originating from the browser.

        +
        CLIENT

        Logs from a WebDriver client.

        +
        DRIVER

        Logs from a WebDriver implementation.

        +
        PERFORMANCE

        Logs related to performance.

        +
        SERVER

        Logs from the remote server.

        +
        \ No newline at end of file diff --git a/docs/enum_webdriver_promise_ControlFlow_EventType.html b/docs/enum_webdriver_promise_ControlFlow_EventType.html index 6947eb6..143fce8 100644 --- a/docs/enum_webdriver_promise_ControlFlow_EventType.html +++ b/docs/enum_webdriver_promise_ControlFlow_EventType.html @@ -1,4 +1,9 @@ -webdriver.promise.ControlFlow.EventType

        Enum webdriver.promise.ControlFlow.EventType

        code »
        Type: string

        Events that may be emitted by an webdriver.promise.ControlFlow.

        Values and Descriptions

        IDLE
        Emitted when all tasks have been successfully executed.
        SCHEDULE_TASK
        Emitted whenever a new task has been scheduled.
        UNCAUGHT_EXCEPTION
        Emitted whenever a control flow aborts due to an unhandled promise - rejection. This event will be emitted along with the offending rejection - reason. Upon emitting this event, the control flow will empty its task - queue and revert to its initial state.
        Show:
        \ No newline at end of file +ControlFlow.EventType

        enum ControlFlow.EventType

        Typestring

        Events that may be emitted by an webdriver.promise.ControlFlow.

        +

        Values and Descriptions

        IDLE

        Emitted when all tasks have been successfully executed.

        +
        RESET

        Emitted when a ControlFlow has been reset.

        +
        SCHEDULE_TASK

        Emitted whenever a new task has been scheduled.

        +
        UNCAUGHT_EXCEPTION

        Emitted whenever a control flow aborts due to an unhandled promise +rejection. This event will be emitted along with the offending rejection +reason. Upon emitting this event, the control flow will empty its task +queue and revert to its initial state.

        +
        \ No newline at end of file diff --git a/docs/enum_webdriver_promise_Deferred_State_.html b/docs/enum_webdriver_promise_Deferred_State_.html index 7468bf8..80ca09e 100644 --- a/docs/enum_webdriver_promise_Deferred_State_.html +++ b/docs/enum_webdriver_promise_Deferred_State_.html @@ -1 +1 @@ -webdriver.promise.Deferred.State_

        Enum webdriver.promise.Deferred.State_

        code »
        Type: number

        The three states a webdriver.promise.Deferred object may be in.

        Values and Descriptions

        Show:
        \ No newline at end of file +webdriver.promise.Deferred.State_

        Enum webdriver.promise.Deferred.State_

        code »
        Type: number

        The three states a webdriver.promise.Deferred object may be in.

        Values and Descriptions

        Show:
        \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 4d91ae5..cd04035 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,60 +1,124 @@ -Index

        selenium-webdriver

        +Index

        selenium-webdriver

        +

        Selenium is a browser automation library. Most often used for testing +web-applications, Selenium may be used for any task that requires automating +interaction with the browser.

        Installation

        -

        Install the latest published version using npm:

        +

        Install via npm with

        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. -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

        -

        To run the tests, you will need to download a copy of the -ChromeDriver 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 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
        -
        +

        Out of the box, Selenium includes everything you need to work with Firefox. You +will need to download additional components to work with the other major +browsers. The drivers for Chrome, IE, PhantomJS, and Opera are all standalone +executables that should be placed on your +PATH. The SafariDriver +browser extension should be installed in your browser before using Selenium; we +recommend disabling the extension when using the browser without Selenium or +installing the extension in a profile only used for testing.

        +
        BrowserComponent
        Chromechromedriver(.exe)
        Internet ExplorerIEDriverServer.exe
        PhantomJSphantomjs(.exe)
        Operaoperadriver(.exe)
        SafariSafariDriver.safariextz

        Usage

        -
        var webdriver = require('selenium-webdriver');
        -
        -var driver = new webdriver.Builder().
        -    withCapabilities(webdriver.Capabilities.chrome()).
        -    build();
        +

        The sample below and others are included in the example directory. You may +also find the tests for selenium-webdriver informative.

        +
        var webdriver = require('selenium-webdriver'),
        +    By = require('selenium-webdriver').By,
        +    until = require('selenium-webdriver').until;
         
        -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);
        +var driver = new webdriver.Builder()
        +    .forBrowser('firefox')
        +    .build();
         
        +driver.get('http://www.google.com/ncr');
        +driver.findElement(By.name('q')).sendKeys('webdriver');
        +driver.findElement(By.name('btnG')).click();
        +driver.wait(until.titleIs('webdriver - Google Search'), 1000);
         driver.quit();
         
        +

        Using the Builder API

        +

        The Builder class is your one-stop shop for configuring new WebDriver +instances. Rather than clutter your code with branches for the various browsers, +the builder lets you set all options in one flow. When you call +Builder#build(), all options irrelevant to the selected browser are dropped:

        +
        var webdriver = require('selenium-webdriver'),
        +    chrome = require('selenium-webdriver/chrome'),
        +    firefox = require('selenium-webdriver/firefox');
        +
        +var driver = new webdriver.Builder()
        +    .forBrowser('firefox')
        +    .setChromeOptions(/* ... */)
        +    .setFirefoxOptions(/* ... */)
        +    .build();
        +
        +

        Why would you want to configure options irrelevant to the target browser? The +Builder's API defines your default configuration. You can change the target +browser at runtime through the SELENIUM_BROWSER environment variable. For +example, the example/google_search.js script is configured to run against +Firefox. You can run the example against other browsers just by changing the +runtime environment

        +
        # cd node_modules/selenium-webdriver
        +node example/google_search
        +SELENIUM_BROWSER=chrome node example/google_search
        +SELENIUM_BROWSER=safari node example/google_search
        +
        +

        The Standalone Selenium Server

        +

        The standalone Selenium Server acts as a proxy between your script and the +browser-specific drivers. The server may be used when running locally, but it's +not recommend as it introduces an extra hop for each request and will slow +things down. The server is required, however, to use a browser on a remote host +(most browser drivers, like the IEDriverServer, do not accept remote +connections).

        +

        To use the Selenium Server, you will need to install the +JDK and +download the latest server from Selenium. Once downloaded, run the +server with

        +
        java -jar selenium-server-standalone-2.45.0.jar
        +
        +

        You may configure your tests to run against a remote server through the Builder +API:

        +
        var driver = new webdriver.Builder()
        +    .forBrowser('firefox')
        +    .usingServer('http://localhost:4444/wd/hub')
        +    .build();
        +
        +

        Or change the Builder's configuration at runtime with the SELENIUM_REMOTE_URL +environment variable:

        +
        SELENIUM_REMOTE_URL="http://localhost:4444/wd/hub" node script.js
        +
        +

        You can experiment with these options using the example/google_search.js +script provided with selenium-webdriver.

        Documentation

        -

        Full documentation is available on the Selenium project wiki.

        +

        API documentation is included in the docs directory and is also available +online from the Selenium project. Addition resources include

        + +

        Contributing

        +

        Contributions are accepted either through GitHub pull requests or patches +via the Selenium issue tracker. You must sign our +Contributor License Agreement before your changes will be accepted.

        Issues

        -

        Please report any issues using the Selenium issue tracker.

        +

        Please report any issues using the Selenium issue tracker. When using +the issue tracker

        +
        • Do include a detailed description of the problem.
        • Do include a link to a gist with any +interesting stack traces/logs (you may also attach these directly to the bug +report).
        • Do include a reduced test case. Reporting "unable to find +element on the page" is not a valid report - there's nothing for us to +look into. Expect your bug report to be closed if you do not provide enough +information for us to investigate.
        • Do not use the issue tracker to submit basic help requests. All help +inquiries should be directed to the user forum or #selenium IRC +channel.
        • Do not post empty "I see this too" or "Any updates?" comments. These +provide no additional information and clutter the log.
        • Do not report regressions on closed bugs as they are not actively +monitored for upates (especially bugs that are >6 months old). Please open a +new issue and reference the original bug in your report.

        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.

        -
        \ No newline at end of file +

        Licensed to the Software Freedom Conservancy (SFC) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The SFC licenses this file +to you 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.

        + \ No newline at end of file diff --git a/docs/interface_goog_debug_EntryPointMonitor.html b/docs/interface_goog_debug_EntryPointMonitor.html new file mode 100644 index 0000000..e730ac1 --- /dev/null +++ b/docs/interface_goog_debug_EntryPointMonitor.html @@ -0,0 +1,11 @@ +goog.debug.EntryPointMonitor

        Interface goog.debug.EntryPointMonitor

        code »
        Show:

        Instance Methods

        Try to remove an instrumentation wrapper created by this monitor. + If the function passed to unwrap is not a wrapper created by this + monitor, then we will do nothing. + + Notice that some wrappers may not be unwrappable. For example, if other + monitors have applied their own wrappers, then it will be impossible to + unwrap them because their wrappers will have captured our wrapper. + + So it is important that entry points are unwrapped in the reverse + order that they were wrapped.

        Parameters
        fn: !Function
        A function to unwrap.
        Returns
        The unwrapped function, or fn if it was not + a wrapped function created by this monitor.
        code »wrap ( fn )!Function

        Instruments a function.

        Parameters
        fn: !Function
        A function to instrument.
        Returns
        The instrumented function.
        \ No newline at end of file diff --git a/docs/interface_goog_disposable_IDisposable.html b/docs/interface_goog_disposable_IDisposable.html new file mode 100644 index 0000000..54a3411 --- /dev/null +++ b/docs/interface_goog_disposable_IDisposable.html @@ -0,0 +1,3 @@ +goog.disposable.IDisposable

        Interface goog.disposable.IDisposable

        code »

        Interface for a disposable object. If a instance requires cleanup + (references COM objects, DOM notes, or other disposable objects), it should + implement this interface (it may subclass goog.Disposable).

        Show:

        Instance Methods

        code »dispose ( )void

        Disposes of the object and its resources.

        Returns
        Nothing.
        Returns
        Whether the object has been disposed of.
        \ No newline at end of file diff --git a/docs/interface_goog_events_Listenable.html b/docs/interface_goog_events_Listenable.html new file mode 100644 index 0000000..e5837e8 --- /dev/null +++ b/docs/interface_goog_events_Listenable.html @@ -0,0 +1,84 @@ +goog.events.Listenable

        Interface goog.events.Listenable

        code »

        A listenable interface. A listenable is an object with the ability + to dispatch/broadcast events to "event listeners" registered via + listen/listenOnce. + + The interface allows for an event propagation mechanism similar + to one offered by native browser event targets, such as + capture/bubble mechanism, stopping propagation, and preventing + default actions. Capture/bubble mechanism depends on the ancestor + tree constructed via #getParentEventTarget; this tree + must be directed acyclic graph. The meaning of default action(s) + in preventDefault is specific to a particular use case. + + Implementations that do not support capture/bubble or can not have + a parent listenable can simply not implement any ability to set the + parent listenable (and have #getParentEventTarget return + null). + + Implementation of this class can be used with or independently from + goog.events. + + Implementation must call #addImplementation(implClass).

        Show:

        Instance Methods

        Dispatches an event (or event like object) and calls all listeners + listening for events of this type. The type of the event is decided by the + type property on the event object. + + If any of the listeners returns false OR calls preventDefault then this + function will return false. If one of the capture listeners calls + stopPropagation, then the bubble listeners won't fire.

        Parameters
        e: goog.events.EventLike
        Event object.
        Returns
        If anyone called preventDefault on the event object (or + if any of the listeners returns false) this will also return false.
        code »<EVENTOBJ> fireListeners ( type, capture, eventObject )boolean

        Fires all registered listeners in this listenable for the given + type and capture mode, passing them the given eventObject. This + does not perform actual capture/bubble. Only implementors of the + interface should be using this.

        Parameters
        type: (string|!goog.events.EventId.<EVENTOBJ>)
        The type of the + listeners to fire.
        capture: boolean
        The capture mode of the listeners to fire.
        eventObject: EVENTOBJ
        The event object to fire.
        Returns
        Whether all listeners succeeded without + attempting to prevent default behavior. If any listener returns + false or called goog.events.Event#preventDefault, this returns + false.
        code »<SCOPE, EVENTOBJ> getListener ( type, listener, capture, opt_listenerScope )goog.events.ListenableKey

        Gets the goog.events.ListenableKey for the event or null if no such + listener is in use.

        Parameters
        type: (string|!goog.events.EventId.<EVENTOBJ>)
        The name of the event + without the 'on' prefix.
        listener: function(this: SCOPE, EVENTOBJ): (boolean|undefined)
        The + listener function to get.
        capture: boolean
        Whether the listener is a capturing listener.
        opt_listenerScope: SCOPE=
        Object in whose scope to call the + listener.
        Returns
        the found listener or null if not found.
        code »<EVENTOBJ> getListeners ( type, capture )!Array.<goog.events.ListenableKey>

        Gets all listeners in this listenable for the given type and + capture mode.

        Parameters
        type: (string|!goog.events.EventId)
        The type of the listeners to fire.
        capture: boolean
        The capture mode of the listeners to fire.
        Returns
        An array of registered + listeners.

        Returns the parent of this event target to use for capture/bubble + mechanism. + + NOTE(user): The name reflects the original implementation of + custom event target (goog.events.EventTarget). We decided + that changing the name is not worth it.

        Returns
        The parent EventTarget or null if + there is no parent.
        code »<EVENTOBJ> hasListener ( opt_type, opt_capture )boolean

        Whether there is any active listeners matching the specified + signature. If either the type or capture parameters are + unspecified, the function will match on the remaining criteria.

        Parameters
        opt_type: (string|!goog.events.EventId.<EVENTOBJ>)=
        Event type.
        opt_capture: boolean=
        Whether to check for capture or bubble + listeners.
        Returns
        Whether there is any active listeners matching + the requested type and/or capture phase.
        code »<SCOPE, EVENTOBJ> listen ( type, listener, opt_useCapture, opt_listenerScope )goog.events.ListenableKey

        Adds an event listener. A listener can only be added once to an + object and if it is added again the key for the listener is + returned. Note that if the existing listener is a one-off listener + (registered via listenOnce), it will no longer be a one-off + listener after a call to listen().

        Parameters
        type: (string|!goog.events.EventId.<EVENTOBJ>)
        The event type id.
        listener: function(this: SCOPE, EVENTOBJ): (boolean|undefined)
        Callback + method.
        opt_useCapture: boolean=
        Whether to fire in capture phase + (defaults to false).
        opt_listenerScope: SCOPE=
        Object in whose scope to call the + listener.
        Returns
        Unique key for the listener.
        code »<SCOPE, EVENTOBJ> listenOnce ( type, listener, opt_useCapture, opt_listenerScope )goog.events.ListenableKey

        Adds an event listener that is removed automatically after the + listener fired once. + + If an existing listener already exists, listenOnce will do + nothing. In particular, if the listener was previously registered + via listen(), listenOnce() will not turn the listener into a + one-off listener. Similarly, if there is already an existing + one-off listener, listenOnce does not modify the listeners (it is + still a once listener).

        Parameters
        type: (string|!goog.events.EventId.<EVENTOBJ>)
        The event type id.
        listener: function(this: SCOPE, EVENTOBJ): (boolean|undefined)
        Callback + method.
        opt_useCapture: boolean=
        Whether to fire in capture phase + (defaults to false).
        opt_listenerScope: SCOPE=
        Object in whose scope to call the + listener.
        Returns
        Unique key for the listener.

        Removes all listeners from this listenable. If type is specified, + it will only remove listeners of the particular type. otherwise all + registered listeners will be removed.

        Parameters
        opt_type: string=
        Type of event to remove, default is to + remove all types.
        Returns
        Number of listeners removed.
        code »<SCOPE, EVENTOBJ> unlisten ( type, listener, opt_useCapture, opt_listenerScope )boolean

        Removes an event listener which was added with listen() or listenOnce().

        Parameters
        type: (string|!goog.events.EventId.<EVENTOBJ>)
        The event type id.
        listener: function(this: SCOPE, EVENTOBJ): (boolean|undefined)
        Callback + method.
        opt_useCapture: boolean=
        Whether to fire in capture phase + (defaults to false).
        opt_listenerScope: SCOPE=
        Object in whose scope to call + the listener.
        Returns
        Whether any listener was removed.

        Removes an event listener which was added with listen() by the key + returned by listen().

        Parameters
        key: goog.events.ListenableKey
        The key returned by + listen() or listenOnce().
        Returns
        Whether any listener was removed.

        Global Functions

        Marks a given class (constructor) as an implementation of + Listenable, do that we can query that fact at runtime. The class + must have already implemented the interface.

        Parameters
        cls: !Function
        The class constructor. The corresponding + class must have already implemented the interface.
        Parameters
        obj: Object
        The object to check.
        Returns
        Whether a given instance implements Listenable. The + class/superclass of the instance must call addImplementation.

        Global Properties

        An expando property to indicate that an object implements + goog.events.Listenable. + + See addImplementation/isImplementedBy.

        \ No newline at end of file diff --git a/docs/interface_goog_events_ListenableKey.html b/docs/interface_goog_events_ListenableKey.html new file mode 100644 index 0000000..53fd38b --- /dev/null +++ b/docs/interface_goog_events_ListenableKey.html @@ -0,0 +1,2 @@ +goog.events.ListenableKey

        Interface goog.events.ListenableKey

        code »

        An interface that describes a single registered listener.

        Show:

        Instance Properties

        Whether the listener works on capture phase.

        The 'this' object for the listener function's scope.

        A globally unique number to identify the key.

        code »listener : (function(?): ?|{handleEvent: function(?): ?}|null)

        The listener function.

        The event type the listener is listening to.

        Global Functions

        Reserves a key to be used for ListenableKey#key field.

        Returns
        A number to be used to fill ListenableKey#key + field.

        Global Properties

        Counter used to create a unique key

        \ No newline at end of file diff --git a/docs/interface_goog_labs_testing_Matcher.html b/docs/interface_goog_labs_testing_Matcher.html index 1db4111..9495c9b 100644 --- a/docs/interface_goog_labs_testing_Matcher.html +++ b/docs/interface_goog_labs_testing_Matcher.html @@ -1,2 +1,2 @@ goog.labs.testing.Matcher

        Interface goog.labs.testing.Matcher

        code »

        A matcher object to be used in assertThat statements.

        Show:

        Instance Methods

        code »describe ( value, opt_description )string

        Describes why the matcher failed.

        Parameters
        value: *
        The value that didn't match.
        opt_description: string=
        A partial description to which the reason - will be appended.
        Returns
        Description of why the matcher failed.
        code »matches ( value )boolean

        Determines whether a value matches the constraints of the match.

        Parameters
        value: *
        The object to match.
        Returns
        Whether the input value matches this matcher.
        \ No newline at end of file + will be appended.Returns
        Description of why the matcher failed.
        code »matches ( value )boolean

        Determines whether a value matches the constraints of the match.

        Parameters
        value: *
        The object to match.
        Returns
        Whether the input value matches this matcher.

        Global Functions

        code »goog.labs.testing.Matcher.makeMatcher ( matchesFunction, opt_describeFunction )!Function

        Generates a Matcher from the ‘matches’ and ‘describe’ functions passed in.

        Parameters
        matchesFunction: !Function
        The ‘matches’ function.
        opt_describeFunction: Function=
        The ‘describe’ function.
        Returns
        The custom matcher.
        \ No newline at end of file diff --git a/docs/interface_goog_net_XhrLike.html b/docs/interface_goog_net_XhrLike.html new file mode 100644 index 0000000..ebff7e3 --- /dev/null +++ b/docs/interface_goog_net_XhrLike.html @@ -0,0 +1,3 @@ +goog.net.XhrLike

        Interface goog.net.XhrLike

        code »

        Interface for the common parts of XMLHttpRequest. + + Mostly copied from externs/w3c_xml.js.

        Show:

        Type Definitions

        Typedef that refers to either native or custom-implemented XHR objects.

        Instance Methods

        Parameters
        header
        code »open ( method, url, opt_async, opt_user, opt_password )
        Parameters
        method
        url
        opt_async
        opt_user
        opt_password
        code »send ( opt_data )
        Parameters
        opt_data
        code »setRequestHeader ( header, value )
        Parameters
        header
        value

        Instance Properties

        \ No newline at end of file diff --git a/docs/interface_goog_structs_Collection.html b/docs/interface_goog_structs_Collection.html new file mode 100644 index 0000000..e94fe48 --- /dev/null +++ b/docs/interface_goog_structs_Collection.html @@ -0,0 +1 @@ +goog.structs.Collection

        Interface goog.structs.Collection.<T>

        code »

        An interface for a collection of values.

        Show:

        Instance Methods

        code »add ( value )
        Parameters
        value: T
        Value to add to the collection.
        code »contains ( value )boolean
        Parameters
        value: T
        Value to find in the collection.
        Returns
        Whether the collection contains the specified value.
        Returns
        The number of values stored in the collection.
        code »remove ( value )
        Parameters
        value: T
        Value to remove from the collection.
        \ No newline at end of file diff --git a/docs/interface_goog_testing_MockInterface.html b/docs/interface_goog_testing_MockInterface.html new file mode 100644 index 0000000..c5af2d4 --- /dev/null +++ b/docs/interface_goog_testing_MockInterface.html @@ -0,0 +1,3 @@ +goog.testing.MockInterface

        Interface goog.testing.MockInterface

        code »
        Show:

        Instance Methods

        Write down all the expected functions that have been called on the + mock so far. From here on out, future function calls will be + compared against this list.

        Reset the mock.

        Assert that the expected function calls match the actual calls.

        \ No newline at end of file diff --git a/docs/interface_webdriver_CommandExecutor.html b/docs/interface_webdriver_CommandExecutor.html index 9b45f41..88db5f1 100644 --- a/docs/interface_webdriver_CommandExecutor.html +++ b/docs/interface_webdriver_CommandExecutor.html @@ -1,5 +1,9 @@ -webdriver.CommandExecutor

        Interface webdriver.CommandExecutor

        code »

        Handles the execution of webdriver.Command objects.

        Show:

        Instance Methods

        code »execute ( command, callback )

        Executes the given 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
        command: !webdriver.Command
        The command to execute.
        callback: function(Error, !bot.response.ResponseObject==)
        the function - to invoke when the command response is ready.
        \ No newline at end of file +CommandExecutor

        interface CommandExecutor

        Handles the execution of WebDriver commands.

        +

        Instance Methods

        execute(command, callback)code »

        Executes the given 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
        commandwebdriver.Command

        The command to execute.

        +
        callbackfunction(Error, {status: number, value: *}=): ?

        the function +to invoke when the command response is ready.

        +
        \ No newline at end of file diff --git a/docs/interface_webdriver_http_Client.html b/docs/interface_webdriver_http_Client.html index 68f0fcc..1f09a13 100644 --- a/docs/interface_webdriver_http_Client.html +++ b/docs/interface_webdriver_http_Client.html @@ -1,6 +1,10 @@ -webdriver.http.Client

        Interface webdriver.http.Client

        code »

        Interface used for sending individual HTTP requests to the server.

        Show:

        Instance Methods

        code »send ( request, callback )

        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 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
        request: !webdriver.http.Request
        The request to send.
        callback: function(Error, !webdriver.http.Response==)
        the function to - invoke when the server's response is ready.
        \ No newline at end of file +Client

        interface Client

        Interface used for sending individual HTTP requests to the server.

        +

        Instance Methods

        send(request, callback)code »

        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 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
        requestwebdriver.http.Request

        The request to send.

        +
        callbackfunction(Error, webdriver.http.Response=): ?

        the function to +invoke when the server's response is ready.

        +
        \ No newline at end of file diff --git a/docs/interface_webdriver_promise_Thenable.html b/docs/interface_webdriver_promise_Thenable.html new file mode 100644 index 0000000..bdc10ca --- /dev/null +++ b/docs/interface_webdriver_promise_Thenable.html @@ -0,0 +1,77 @@ +Thenable

        interface Thenable<T>

        All extended interfaces
        IThenable<T>

        Instance Methods

        cancel(opt_reason)code »

        Cancels the computation of this promise's value, rejecting the promise in +the process. This method is a no-op if the promise has already been +resolved.

        +
        Parameters
        opt_reason?(string|webdriver.promise.CancellationError)=

        The reason this +promise is being cancelled.

        +

        isPending()code »

        Returns
        boolean

        Whether this promise's value is still being computed.

        +

        <R> then(opt_callback, opt_errback)code »

        Registers listeners for when this instance is resolved.

        +

        Specified by: IThenable

        Parameters
        opt_callback?function(T): (R|IThenable<R>)=

        The +function to call if this promise is successfully resolved. The function +should expect a single argument: the promise's resolved value.

        +
        opt_errback?function(*): (R|IThenable<R>)=

        The function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

        +
        Returns
        webdriver.promise.Promise

        A new promise which will be +resolved with the result of the invoked callback.

        +

        <R> thenCatch(errback)code »

        Registers a listener for when this promise is rejected. This is synonymous +with the 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
        errbackfunction(*): (R|IThenable<R>)

        The +function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

        +
        Returns
        webdriver.promise.Promise

        A new promise which will be +resolved with the result of the invoked callback.

        +

        <R> thenFinally(callback)code »

        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 finally clause in a synchronous API:

        +
        // Synchronous API:
        +try {
        +  doSynchronousWork();
        +} finally {
        +  cleanUp();
        +}
        +
        +// Asynchronous promise API:
        +doAsynchronousWork().thenFinally(cleanUp);
        +
        +

        Note: similar to the 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
        +}
        +
        +promise.rejected(Error('one'))
        +    .thenFinally(function() {
        +      throw Error('two');  // Hides Error: one
        +    });
        +
        +
        Parameters
        callbackfunction(): (R|IThenable<R>)

        The function +to call when this promise is resolved.

        +
        Returns
        webdriver.promise.Promise

        A promise that will be fulfilled +with the callback result.

        +

        Functions

        Thenable.addImplementation(ctor)code »

        Adds a property to a class prototype to allow runtime checks of whether +instances of that class implement the Thenable interface. This function +will also ensure the prototype's then function is exported from +compiled code.

        +
        Parameters
        ctorfunction(new: webdriver.promise.Thenable, ...?): ?

        The +constructor whose prototype to modify.

        +

        Thenable.isImplementation(object)code »

        Checks if an object has been tagged for implementing the Thenable +interface as defined by +webdriver.promise.Thenable.addImplementation.

        +
        Parameters
        object*

        The object to test.

        +
        Returns
        boolean

        Whether the object is an implementation of the Thenable +interface.

        +
        \ No newline at end of file diff --git a/docs/license.html b/docs/license.html index 962258d..3c544de 100644 --- a/docs/license.html +++ b/docs/license.html @@ -202,4 +202,4 @@ See the License for the specific language governing permissions and limitations under the License. - \ No newline at end of file + \ No newline at end of file diff --git a/docs/module_selenium-webdriver.html b/docs/module_selenium-webdriver.html index 2764d07..5478878 100644 --- a/docs/module_selenium-webdriver.html +++ b/docs/module_selenium-webdriver.html @@ -1,4 +1,23 @@ -selenium-webdriver

        Module selenium-webdriver

        code »

        The main user facing module. Exports WebDriver's primary - public API and provides convenience assessors to certain sub-modules.

        Classes

        ActionSequence
        Class for defining sequences of complex user interactions.
        Builder
        Creates new WebDriver instances.
        Capabilities
        No Description.
        Command
        Describes a command to be executed by the WebDriverJS framework.
        EventEmitter
        Object that can emit events for others to listen for.
        Session
        Contains information about a WebDriver session.
        WebDriver
        Creates a new WebDriver client, which provides control over a browser.
        WebElement
        Represents a DOM element.

        Enumerations

        Browser
        Recognized browser names.
        Button
        Enumeration of the buttons used in the advanced interactions API.
        Capability
        Common webdriver capability keys.
        CommandName
        Enumeration of predefined names command names that all command processors - will support.
        Key
        Representations of pressable keys that aren't text.
        Show:

        Properties

        A collection of factory functions for creating webdriver.Locator - instances.

        \ No newline at end of file +selenium-webdriver

        module selenium-webdriver

        The main user facing module. Exports WebDriver's primary +public API and provides convenience assessors to certain sub-modules.

        +

        Types

        ActionSequence

        Class for defining sequences of complex user interactions.

        +
        Browser

        Recognized browser names.

        +
        Builder

        Creates new WebDriver instances.

        +
        Button

        Enumeration of the buttons used in the advanced interactions API.

        +
        Capabilities

        No description.

        +
        Capability

        Common webdriver capability keys.

        +
        Command

        Describes a command to be executed by the WebDriverJS framework.

        +
        CommandName

        Enumeration of predefined names command names that all command processors +will support.

        +
        EventEmitter

        Object that can emit events for others to listen for.

        +
        FileDetector

        Used with WebElement#sendKeys on file +input elements (<input type="file">) to detect when the entered key +sequence defines the path to a file.

        +
        Key

        Representations of pressable keys that aren't text.

        +
        Serializable

        Defines an object that can be asynchronously serialized to its WebDriver +wire representation.

        +
        Session

        Contains information about a WebDriver session.

        +
        WebDriver

        Creates a new WebDriver client, which provides control over a browser.

        +
        WebElement

        Represents a DOM element.

        +
        WebElementPromise

        WebElementPromise is a promise that will be fulfilled with a WebElement.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver__base.html b/docs/module_selenium-webdriver__base.html index 02cc0a1..8806b9a 100644 --- a/docs/module_selenium-webdriver__base.html +++ b/docs/module_selenium-webdriver__base.html @@ -1,14 +1,23 @@ -selenium-webdriver/_base

        Module selenium-webdriver/_base

        code »

        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 - 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.

        Show:

        Functions

        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 "_".

        Parameters
        symbol: string
        The symbol to load. This must resolve to an object.
        Returns
        An object with the exported API.
        Throws
        Error
        If the symbol has not been defined or does not resolve to - an object.
        Returns
        Whether this script was loaded in dev mode.
        code »require ( symbol )

        Loads a symbol by name from the protected Closure context.

        Parameters
        symbol: string
        The symbol to load.
        Returns
        The loaded symbol, or null if not found.
        Throws
        Error
        If the symbol has not been defined.

        Properties

        \ No newline at end of file +selenium-webdriver/_base

        module selenium-webdriver/_base

        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 +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. +

        Functions

        exportPublicApi(symbol)code »

        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 "_".

        +
        Parameters
        symbolstring

        The symbol to load. This must resolve to an object.

        +
        Returns
        Object

        An object with the exported API.

        +
        Throws
        Error

        If the symbol has not been defined or does not resolve to +an object.

        +

        isDevMode()code »

        Returns
        boolean

        Whether this script was loaded in dev mode.

        +

        require(symbol)code »

        Loads a symbol by name from the protected Closure context.

        +
        Parameters
        symbolstring

        The symbol to load.

        +
        Returns

        The loaded symbol, or null if not found.

        +
        Throws
        Error

        If the symbol has not been defined.

        +

        Properties

        closure?
        No description.

        Types

        Context

        Maintains a unique context for Closure library-based code.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver__base_class_Context.html b/docs/module_selenium-webdriver__base_class_Context.html new file mode 100644 index 0000000..03304a3 --- /dev/null +++ b/docs/module_selenium-webdriver__base_class_Context.html @@ -0,0 +1,5 @@ +Context

        class Context

        Maintains a unique context for Closure library-based code.

        +

        new Context(opt_configureForTesting)

        Parameters
        opt_configureForTestingboolean=

        Whether to configure a fake DOM +for Closure-testing code that (incorrectly) assumes a DOM is always +present.

        +

        Instance Properties

        closure?
        No description.
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_builder.html b/docs/module_selenium-webdriver_builder.html index 6b20841..04aa449 100644 --- a/docs/module_selenium-webdriver_builder.html +++ b/docs/module_selenium-webdriver_builder.html @@ -1 +1,2 @@ -selenium-webdriver/builder

        Module selenium-webdriver/builder

        code »

        Classes

        Builder
        Creates new WebDriver instances.
        Show:
        \ No newline at end of file +selenium-webdriver/builder
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_builder_class_Builder.html b/docs/module_selenium-webdriver_builder_class_Builder.html index bac7e53..33a1d06 100644 --- a/docs/module_selenium-webdriver_builder_class_Builder.html +++ b/docs/module_selenium-webdriver_builder_class_Builder.html @@ -1,13 +1,138 @@ -Builder

        Class Builder

        code »
        webdriver.AbstractBuilder
        -  └ Builder

        Creates new WebDriver instances.

        Constructor

        Builder ( )
        Show:

        Instance Methods

        Defined in Builder

        code »build ( )webdriver.WebDriver
        code »setChromeOptions ( options )!Builder

        Sets Chrome-specific options for drivers created by this builder.

        Parameters
        options: !chrome.Options
        The ChromeDriver options to use.
        Returns
        A self reference.
        code »setProxy ( config )!Builder

        Sets the proxy configuration to use for WebDriver clients created by this - builder. Any calls to #withCapabilities after this function will - overwrite these settings.

        Parameters
        config: !proxy.ProxyConfig
        The configuration to use.
        Returns
        A self reference.

        Defined in webdriver.AbstractBuilder

        Returns
        The current desired capabilities for this - builder.
        Returns
        The URL of the WebDriver server this instance is configured - to use.

        Configures which WebDriver server should be used for new sessions. Overrides - the value loaded from the webdriver.AbstractBuilder.SERVER_URL_ENV - upon creation of this instance.

        Parameters
        url: string
        URL of the server to use.
        Returns
        This Builder instance for chain calling.

        Sets the desired capabilities when requesting a new session. This will - overwrite any previously set desired capabilities.

        Parameters
        capabilities: !(Object|webdriver.Capabilities)
        The desired - capabilities for a new session.
        Returns
        This Builder instance for chain calling.

        Instance Properties

        Defined in webdriver.AbstractBuilder

        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 webdriver.AbstractBuilder.SERVER_URL_ENV environment - variable, but may be overridden using - webdriver.AbstractBuilder#usingServer.

        \ No newline at end of file +Builder

        class Builder

        Creates new WebDriver instances. The environment +variables listed below may be used to override a builder's configuration, +allowing quick runtime changes.

        +
        • +

          SELENIUM_BROWSER: defines the target browser in the form +browser[:version][:platform].

          +
        • +

          SELENIUM_REMOTE_URL: defines the remote URL for all builder +instances. This environment variable should be set to a fully qualified +URL for a WebDriver server (e.g. http://localhost:4444/wd/hub). This +option always takes precedence over SELENIUM_SERVER_JAR.

          +
        • +

          SELENIUM_SERVER_JAR: defines the path to the + +standalone Selenium server jar to use. The server will be started the +first time a WebDriver instance and be killed when the process exits.

          +
        +

        Suppose you had mytest.js that created WebDriver with

        +
        var driver = new webdriver.Builder()
        +    .forBrowser('chrome')
        +    .build();
        +
        +

        This test could be made to use Firefox on the local machine by running with +SELENIUM_BROWSER=firefox node mytest.js. Rather than change the code to +target Google Chrome on a remote machine, you can simply set the +SELENIUM_BROWSER and SELENIUM_REMOTE_URL environment variables:

        +
        SELENIUM_BROWSER=chrome:36:LINUX \
        +SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \
        +node mytest.js
        +
        +

        You could also use a local copy of the standalone Selenium server:

        +
        SELENIUM_BROWSER=chrome:36:LINUX \
        +SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \
        +node mytest.js
        +
        +

        new Builder()

        Parameters
        None.

        Instance Methods

        build()code »

        Creates a new WebDriver client based on this builder's current +configuration.

        +
        Returns
        webdriver.WebDriver

        A new WebDriver instance.

        +
        Throws
        Error

        If the current configuration is invalid.

        +

        disableEnvironmentOverrides()code »

        Configures this builder to ignore any environment variable overrides and to +only use the configuration specified through this instance's API.

        +
        Returns
        Builder

        A self reference.

        +

        forBrowser(name, opt_version, opt_platform)code »

        Configures the target browser for clients created by this instance. +Any calls to #withCapabilities after this function will +overwrite these settings.

        +

        You may also define the target browser using the SELENIUM_BROWSER +environment variable. If set, this environment variable should be of the +form browser[:[version][:platform]].

        +
        Parameters
        namestring

        The name of the target browser; +common defaults are available on the webdriver.Browser enum.

        +
        opt_versionstring=

        A desired version; may be omitted if any +version should be used.

        +
        opt_platformstring=

        The desired platform; may be omitted if any +version may be used.

        +
        Returns
        Builder

        A self reference.

        +

        getCapabilities()code »

        Returns the base set of capabilities this instance is currently configured +to use.

        +
        Returns
        webdriver.Capabilities

        The current capabilities for this builder.

        +

        getServerUrl()code »

        Returns
        string

        The URL of the WebDriver server this instance is configured +to use.

        +

        getWebDriverProxy()code »

        Returns
        string

        The URL of the proxy server to use for the WebDriver's HTTP +connections.

        +

        setAlertBehavior(beahvior)code »

        Sets the default action to take with an unexpected alert before returning +an error.

        +
        Parameters
        beahviorstring

        The desired behavior; should be "accept", "dismiss", +or "ignore". Defaults to "dismiss".

        +
        Returns
        Builder

        A self reference.

        +

        setChromeOptions(options)code »

        Sets Chrome specific options +for drivers created by this builder. Any logging or proxy settings defined +on the given options will take precedence over those set through +#setLoggingPrefs and #setProxy, respectively.

        +
        Parameters
        optionschrome.Options

        The ChromeDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setControlFlow(flow)code »

        Sets the control flow that created drivers should execute actions in. If +the flow is never set, or is set to null, it will use the active +flow at the time #build() is called.

        +
        Parameters
        flowwebdriver.promise.ControlFlow

        The control flow to use, or +null to

        +
        Returns
        Builder

        A self reference.

        +

        setEnableNativeEvents(enabled)code »

        Sets whether native events should be used.

        +
        Parameters
        enabledboolean

        Whether to enable native events.

        +
        Returns
        Builder

        A self reference.

        +

        setFirefoxOptions(options)code »

        Sets Firefox specific options +for drivers created by this builder. Any logging or proxy settings defined +on the given options will take precedence over those set through +#setLoggingPrefs and #setProxy, respectively.

        +
        Parameters
        optionsfirefox.Options

        The FirefoxDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setIeOptions(options)code »

        Sets Internet Explorer specific +options for drivers created by +this builder. Any proxy settings defined on the given options will take +precedence over those set through #setProxy.

        +
        Parameters
        optionsie.Options

        The IEDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setLoggingPrefs(prefs)code »

        Sets the logging preferences for the created session. Preferences may be +changed by repeated calls, or by calling #withCapabilities.

        +
        Parameters
        prefs(webdriver.logging.Preferences|Object<string, string>)

        The +desired logging preferences.

        +
        Returns
        Builder

        A self reference.

        +

        setOperaOptions(options)code »

        Sets Opera specific options for +drivers created by this builder. Any logging or proxy settings defined on the +given options will take precedence over those set through +#setLoggingPrefs and #setProxy, respectively.

        +
        Parameters
        optionsopera.Options

        The OperaDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setProxy(config)code »

        Sets the proxy configuration to use for WebDriver clients created by this +builder. Any calls to #withCapabilities after this function will +overwrite these settings.

        +
        Parameters
        config{proxyType: string}

        The configuration to use.

        +
        Returns
        Builder

        A self reference.

        +

        setSafariOptions(options)code »

        Sets Safari specific options +for drivers created by this builder. Any logging settings defined on the +given options will take precedence over those set through +#setLoggingPrefs.

        +
        Parameters
        optionssafari.Options

        The Safari options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setScrollBehavior(behavior)code »

        Sets how elements should be scrolled into view for interaction.

        +
        Parameters
        behaviornumber

        The desired scroll behavior: either 0 to align with +the top of the viewport or 1 to align with the bottom.

        +
        Returns
        Builder

        A self reference.

        +

        usingServer(url)code »

        Sets the URL of a remote WebDriver server to use. Once a remote URL has been +specified, the builder direct all new clients to that server. If this method +is never called, the Builder will attempt to create all clients locally.

        +

        As an alternative to this method, you may also set the SELENIUM_REMOTE_URL +environment variable.

        +
        Parameters
        urlstring

        The URL of a remote server to use.

        +
        Returns
        Builder

        A self reference.

        +

        usingWebDriverProxy(proxy)code »

        Sets the URL of the proxy to use for the WebDriver's HTTP connections. +If this method is never called, the Builder will create a connection without +a proxy.

        +
        Parameters
        proxystring

        The URL of a proxy to use.

        +
        Returns
        Builder

        A self reference.

        +

        withCapabilities(capabilities)code »

        Sets the desired capabilities when requesting a new session. This will +overwrite any previously set capabilities.

        +
        Parameters
        capabilitiesObject

        The desired +capabilities for a new session.

        +
        Returns
        Builder

        A self reference.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_chrome.html b/docs/module_selenium-webdriver_chrome.html index f5d6a01..ce0ec26 100644 --- a/docs/module_selenium-webdriver_chrome.html +++ b/docs/module_selenium-webdriver_chrome.html @@ -1,5 +1,90 @@ -selenium-webdriver/chrome

        Module selenium-webdriver/chrome

        code »

        Classes

        Options
        Class for managing ChromeDriver specific options.
        ServiceBuilder
        Creates remote.DriverService instances that manage a ChromeDriver - server.
        Show:

        Functions

        code »createDriver ( opt_options, opt_service )!webdriver.WebDriver

        Creates a new ChromeDriver session.

        Parameters
        opt_options: (webdriver.Capabilities|Options)=
        The session options.
        opt_service: remote.DriverService=
        The session to use; will use - the default service by default.
        Returns
        A new WebDriver instance.
        code »getDefaultService ( )!remote.DriverService

        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.

        Returns
        The default ChromeDriver service.

        Sets the default service to use for new ChromeDriver instances.

        Parameters
        service: !remote.DriverService
        The service to use.
        Throws
        Error
        If the default service is currently running.
        \ No newline at end of file +selenium-webdriver/chrome

        module selenium-webdriver/chrome

        Defines a WebDriver client for the Chrome +web browser. Before using this module, you must download the latest +ChromeDriver release and ensure it can be found on your system PATH.

        +

        There are three primary classes exported by this module:

        +
        1. +

          ServiceBuilder: configures the +remote.DriverService +that manages the ChromeDriver child process.

          +
        2. +

          Options: defines configuration options for each new Chrome +session, such as which proxy to use, +what extensions to install, or +what command-line switches to use when +starting the browser.

          +
        3. +

          Driver: the WebDriver client; each new instance will control +a unique browser session with a clean user profile (unless otherwise +configured through the Options class).

          +
        +

        Customizing the ChromeDriver Server

        +

        By default, every Chrome session will use a single driver service, which is +started the first time a Driver instance is created and terminated +when this process exits. The default service will inherit its environment +from the current process and direct all output to /dev/null. You may obtain +a handle to this default service using +getDefaultService() and change its configuration +with setDefaultService().

        +

        You may also create a Driver with its own driver service. This is +useful if you need to capture the server's log output for a specific session:

        +
        var chrome = require('selenium-webdriver/chrome');
        +
        +var service = new chrome.ServiceBuilder()
        +    .loggingTo('/my/log/file.txt')
        +    .enableVerboseLogging()
        +    .build();
        +
        +var options = new chrome.Options();
        +// configure browser options ...
        +
        +var driver = new chrome.Driver(options, service);
        +
        +

        Users should only instantiate the Driver class directly when they +need a custom driver service configuration (as shown above). For normal +operation, users should start Chrome using the +selenium-webdriver.Builder.

        +

        Working with Android

        +

        The ChromeDriver supports running tests on the Chrome browser as +well as WebView apps starting in Android 4.4 (KitKat). In order to +work with Android, you must first start the adb

        +
        adb start-server
        +
        +

        By default, adb will start on port 5037. You may change this port, but this +will require configuring a custom server that will connect +to adb on the correct port:

        +
        var service = new chrome.ServiceBuilder()
        +    .setAdbPort(1234)
        +    build();
        +// etc.
        +
        +

        The ChromeDriver may be configured to launch Chrome on Android using +Options#androidChrome():

        +
        var driver = new Builder()
        +    .forBrowser('chrome')
        +    .setChromeOptions(new chrome.Options().androidChrome())
        +    .build();
        +
        +

        Alternatively, you can configure the ChromeDriver to launch an app with a +Chrome-WebView by setting the androidActivity option:

        +
        var driver = new Builder()
        +    .forBrowser('chrome')
        +    .setChromeOptions(new chrome.Options()
        +        .androidPackage('com.example')
        +        .androidActivity('com.example.Activity'))
        +    .build();
        +
        +

        [Refer to the ChromeDriver site] for more information on using the +ChromeDriver with Android.

        +

        Functions

        getDefaultService()code »

        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.

        +
        Returns
        DriverService

        The default ChromeDriver service.

        +

        setDefaultService(service)code »

        Sets the default service to use for new ChromeDriver instances.

        +
        Parameters
        serviceDriverService

        The service to use.

        +
        Throws
        Error

        If the default service is currently running.

        +

        Types

        Driver

        Creates a new WebDriver client for Chrome.

        +
        Options

        Class for managing ChromeDriver specific options.

        +
        ServiceBuilder

        Creates selenium-webdriver/remote.DriverService instances that manage +a ChromeDriver +server in a child process.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_chrome_class_Driver.html b/docs/module_selenium-webdriver_chrome_class_Driver.html new file mode 100644 index 0000000..61b84dc --- /dev/null +++ b/docs/module_selenium-webdriver_chrome_class_Driver.html @@ -0,0 +1,280 @@ +Driver

        class Driver

        webdriver.WebDriver
        +  └ Driver

        Creates a new WebDriver client for Chrome.

        +

        new Driver(opt_config, opt_service, opt_flow)

        Parameters
        opt_config?(Capabilities|Options)=

        The configuration +options.

        +
        opt_service?DriverService=

        The session to use; will use +the default service by default.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The control flow to use, or +null to use the currently active flow.

        +

        Instance Methods

        actions()code »

        Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

        +
        driver.actions().
        +    mouseDown(element1).
        +    mouseMove(element2).
        +    mouseUp().
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.ActionSequence

        A new action sequence for this instance.

        +

        <T> call(fn, opt_scope, var_args)code »

        Schedules a command to execute a custom function.

        +

        Defined by: webdriver.WebDriver

        Parameters
        fnfunction(...?): (T|webdriver.promise.Promise<T>)

        The function to +execute.

        +
        opt_scope?Object=

        The object in whose scope to execute the function.

        +
        var_args...*

        Any arguments to pass to the function.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved' +with the function's result.

        +

        close()code »

        Schedules a command to close the current window.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when this command has completed.

        +

        controlFlow()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.ControlFlow

        The control flow used by this +instance.

        +

        <T> executeAsyncScript(script, var_args)code »

        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 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 #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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Example #1: Performing a sleep that is synchronized with the currently +selected window:

        +
        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 #2: Synchronizing a test with an AJAX application:

        +
        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');
        +
        +

        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.

        +
        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.responseText);
        +    }
        +  }
        +  xhr.send('');
        +}).then(function(str) {
        +  console.log(JSON.parse(str)['food']);
        +});
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        <T> executeScript(script, var_args)code »

        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 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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        findElement(locator)code »

        Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator to use.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedule a command to search for multiple elements on the page.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        get(url)code »

        Schedules a command to navigate to the given URL.

        +

        Defined by: webdriver.WebDriver

        Parameters
        urlstring

        The fully qualified URL to open.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the document has finished loading.

        +

        getAllWindowHandles()code »

        Schedules a command to retrieve the current list of available window handles.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<Array<string>>

        A promise that will +be resolved with an array of window handles.

        +

        getCapabilities()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Capabilities>

        A promise +that will resolve with the this instance's capabilities.

        +

        getCurrentUrl()code »

        Schedules a command to retrieve the URL of the current page.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current URL.

        +

        getPageSource()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page source.

        +

        getSession()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Session>

        A promise for this +client's session.

        +

        getTitle()code »

        Schedules a command to retrieve the current page's title.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page's title.

        +

        getWindowHandle()code »

        Schedules a command to retrieve they current window handle.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current window handle.

        +

        isElementPresent(locatorOrElement)code »

        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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator to use, or the actual +DOM element to be located by the server.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the element is present on the page.

        +

        launchApp(id)code »

        Schedules a command to launch Chrome App with given ID.

        +
        Parameters
        idstring

        ID of the App to launch.

        +
        Returns
        webdriver.promise.Promise

        A promise that will be resolved +when app is launched.

        +

        manage()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.Options

        The options interface for this +instance.

        +


        quit()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the command has completed.

        +

        <T> schedule(command, description)code »

        Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

        +

        Defined by: webdriver.WebDriver

        Parameters
        commandwebdriver.Command

        The command to schedule.

        +
        descriptionstring

        A description of the command for debugging.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved +with the command result.

        +

        setFileDetector(detector)code »

        This function is a no-op as file detectors are not supported by this +implementation.

        +

        Overrides: webdriver.WebDriver

        Parameters
        detectorwebdriver.FileDetector

        The detector to use or null.

        +

        sleep(ms)code »

        Schedules a command to make the driver sleep for the given amount of time.

        +

        Defined by: webdriver.WebDriver

        Parameters
        msnumber

        The amount of time, in milliseconds, to sleep.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the sleep has finished.

        +

        switchTo()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.TargetLocator

        The target locator interface for +this instance.

        +

        takeScreenshot()code »

        Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

        +
        1. Entire page +
        2. Current window +
        3. Visible portion of the current frame +
        4. The screenshot of the entire display containing the browser +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

        +

        touchActions()code »

        Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

        +
        driver.touchActions().
        +    tap(element1).
        +    doubleTap(element2).
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.TouchSequence

        A new touch sequence for this instance.

        +

        <T> wait(condition, opt_timeout, opt_message)code »

        Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

        +

        For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

        +

        Example: waiting up to 10 seconds for an element to be present and visible +on the page.

        +
        var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        +button.click();
        +
        +

        This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

        +

        Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

        +
        var started = startTestServer();
        +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        +driver.get(getServerUrl());
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

        The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

        +
        opt_timeoutnumber=

        How long to wait for the condition to be true.

        +
        opt_messagestring=

        An optional message to use if the wait times +out.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_chrome_class_Options.html b/docs/module_selenium-webdriver_chrome_class_Options.html index 0e21c20..4b92901 100644 --- a/docs/module_selenium-webdriver_chrome_class_Options.html +++ b/docs/module_selenium-webdriver_chrome_class_Options.html @@ -1,22 +1,127 @@ -Options

        Class Options

        code »

        Class for managing ChromeDriver specific options.

        Constructor

        Options ( )
        Show:

        Instance Methods

        code »addArguments ( var_args )!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".

        Parameters
        var_args: ...(string|!Array.<string>)
        The arguments to add.
        Returns
        A self reference.
        code »addExtensions ( var_args )!Options

        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.

        Parameters
        var_args: ...(string|!Buffer|!Array)
        The - extensions to add.
        Returns
        A self reference.
        code »detachDriver ( detach )!Options

        Sets whether to leave the started Chrome browser running if the controlling - ChromeDriver service is killed before webdriver.WebDriver#quit() is - called.

        Parameters
        detach: boolean
        Whether to leave the browser running if the - chromedriver service is killed before the session.
        Returns
        A self reference.
        code »setChromeBinaryPath ( path )!Options

        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"). +Options

        class Options

        webdriver.Serializable
        +  └ Options

        Class for managing ChromeDriver specific options.

        +

        new Options()

        Parameters
        None.

        Instance Methods

        addArguments(var_args)code »

        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".

        +
        Parameters
        var_args...(string|Array<string>)

        The arguments to add.

        +
        Returns
        Options

        A self reference.

        +

        addExtensions(var_args)code »

        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.

        +
        Parameters
        var_args...(string|Buffer|Array<(string|Buffer)>)

        The +extensions to add.

        +
        Returns
        Options

        A self reference.

        +

        androidActivity(name)code »

        Sets the name of the activity hosting a Chrome-based Android WebView. This +option must be set to connect to an Android WebView

        +
        Parameters
        namestring

        The activity name.

        +
        Returns
        Options

        A self reference.

        +

        androidChrome()code »

        Configures the ChromeDriver to launch Chrome on Android via adb. This +function is shorthand for +options.androidPackage('com.android.chrome').

        +
        Returns
        Options

        A self reference.

        +

        androidDeviceSerial(serial)code »

        Sets the device serial number to connect to via ADB. If not specified, the +ChromeDriver will select an unused device at random. An error will be +returned if all devices already have active sessions.

        +
        Parameters
        serialstring

        The device serial number to connect to.

        +
        Returns
        Options

        A self reference.

        +

        androidPackage(pkg)code »

        Sets the package name of the Chrome or WebView app.

        +
        Parameters
        pkg?string

        The package to connect to, or null to disable Android +and switch back to using desktop Chrome.

        +
        Returns
        Options

        A self reference.

        +

        androidProcess(processName)code »

        Sets the process name of the Activity hosting the WebView (as given by ps). +If not specified, the process name is assumed to be the same as +#androidPackage.

        +
        Parameters
        processNamestring

        The main activity name.

        +
        Returns
        Options

        A self reference.

        +

        androidUseRunningApp(useRunning)code »

        Sets whether to connect to an already-running instead of the specified +app instead of launching the app with a clean +data directory.

        +
        Parameters
        useRunningboolean

        Whether to connect to a running instance.

        +
        Returns
        Options

        A self reference.

        +

        detachDriver(detach)code »

        Sets whether to leave the started Chrome browser running if the controlling +ChromeDriver service is killed before webdriver.WebDriver#quit() is +called.

        +
        Parameters
        detachboolean

        Whether to leave the browser running if the +chromedriver service is killed before the session.

        +
        Returns
        Options

        A self reference.

        +

        excludeSwitches(var_args)code »

        List of Chrome command line switches to exclude that ChromeDriver by default +passes when starting Chrome. Do not prefix switches with "--".

        +
        Parameters
        var_args...(string|Array<string>)

        The switches to exclude.

        +
        Returns
        Options

        A self reference.

        +

        serialize()code »

        Converts this instance to its JSON wire protocol representation. Note this +function is an implementation not intended for general use.

        +

        Overrides: webdriver.Serializable

        Returns
        {args: Array<string>, binary: (string|undefined), detach: boolean, extensions: Array<(string|webdriver.promise.Promise)>, localState: ?(Object), logPath: (string|undefined), prefs: ?(Object)}

        The JSON wire protocol representation +of this instance.

        +

        setChromeBinaryPath(path)code »

        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.

        +
        Parameters
        pathstring

        The path to the Chrome binary to use.

        +
        Returns
        Options

        A self reference.

        +

        setChromeLogFile(path)code »

        Sets the path to Chrome's log file. This path should exist on the machine +that will launch Chrome.

        +
        Parameters
        pathstring

        Path to the log file to use.

        +
        Returns
        Options

        A self reference.

        +

        setChromeMinidumpPath(path)code »

        Sets the directory to store Chrome minidumps in. This option is only +supported when ChromeDriver is running on Linux.

        +
        Parameters
        pathstring

        The directory path.

        +
        Returns
        Options

        A self reference.

        +

        setLocalState(state)code »

        Sets preferences for the "Local State" file in Chrome's user data +directory.

        +
        Parameters
        stateObject

        Dictionary of local state preferences.

        +
        Returns
        Options

        A self reference.

        +

        setLoggingPrefs(prefs)code »

        Sets the logging preferences for the new session.

        +
        Parameters
        prefswebdriver.logging.Preferences

        The logging preferences.

        +
        Returns
        Options

        A self reference.

        +

        setMobileEmulation(config)code »

        Configures Chrome to emulate a mobile device. For more information, refer to +the ChromeDriver project page on mobile emulation. Configuration +options include:

        +
        • deviceName: The name of a pre-configured emulated device
        • width: screen width, in pixels
        • height: screen height, in pixels
        • pixelRatio: screen pixel ratio
        +

        Example 1: Using a Pre-configured Device

        +
        var options = new chrome.Options().setMobileEmulation(
        +    {deviceName: 'Google Nexus 5'});
         
        - The binary path be absolute or relative to the chromedriver server
        - executable, but it must exist on the machine that will launch Chrome.
        Parameters
        path: string
        The path to the Chrome binary to use.
        Returns
        A self reference.
        code »setChromeLogFile ( path )!Options

        Sets the path to Chrome's log file. This path should exist on the machine - that will launch Chrome.

        Parameters
        path: string
        Path to the log file to use.
        Returns
        A self reference.
        code »setLocalState ( state )!Options

        Sets preferences for the "Local State" file in Chrome's user data - directory.

        Parameters
        state: !Object
        Dictionary of local state preferences.
        Returns
        A self reference.
        code »setLoggingPreferences ( prefs )!Options

        Sets the logging preferences for the new session.

        Parameters
        prefs: !webdriver.logging.Preferences
        The logging preferences.
        Returns
        A self reference.
        code »setProxy ( proxy )!Options

        Sets the proxy settings for the new session.

        Parameters
        proxy: ProxyConfig
        The proxy configuration to use.
        Returns
        A self reference.
        code »setUserPreferences ( prefs )!Options

        Sets the user preferences for Chrome's user profile. See the "Preferences" - file in Chrome's user data directory for examples.

        Parameters
        prefs: !Object
        Dictionary of user preferences to use.
        Returns
        A self reference.

        Converts this options instance to a webdriver.Capabilities object.

        Parameters
        opt_capabilities: webdriver.Capabilities=
        The capabilities to merge - these options into, if any.
        Returns
        The capabilities.
        code »toJSON ( ){args: !Array.<string>, binary: (string|undefined), detach: boolean, extensions: !Array.<string>, localState: (Object|undefined), logFile: (string|undefined), prefs: (Object|undefined)}

        Converts this instance to its JSON wire protocol representation. Note this - function is an implementation not intended for general use.

        Returns
        The JSON wire protocol representation - of this instance.

        Instance Properties

        Static Functions

        code »Options.fromCapabilities ( capabilities )!Options

        Extracts the ChromeDriver specific options from the given capabilities - object.

        Parameters
        capabilities: !webdriver.Capabilities
        The capabilities object.
        Returns
        The ChromeDriver options.
        \ No newline at end of file +var driver = new chrome.Driver(options); + +

        Example 2: Using Custom Screen Configuration

        +
        var options = new chrome.Options().setMobileEmulation({
        +    width: 360,
        +    height: 640,
        +    pixelRatio: 3.0
        +});
        +
        +var driver = new chrome.Driver(options);
        +
        +
        Parameters
        config?({deviceName: string}|{height: number, pixelRatio: number, width: number})

        The +mobile emulation configuration, or null to disable emulation.

        +
        Returns
        Options

        A self reference.

        +

        setPerfLoggingPrefs(prefs)code »

        Sets the performance logging preferences. Options include:

        +
        • enableNetwork: Whether or not to collect events from Network domain.
        • enablePage: Whether or not to collect events from Page domain.
        • enableTimeline: Whether or not to collect events from Timeline domain. +Note: when tracing is enabled, Timeline domain is implicitly disabled, +unless enableTimeline is explicitly set to true.
        • tracingCategories: A comma-separated string of Chrome tracing categories +for which trace events should be collected. An unspecified or empty +string disables tracing.
        • bufferUsageReportingInterval: The requested number of milliseconds +between DevTools trace buffer usage events. For example, if 1000, then +once per second, DevTools will report how full the trace buffer is. If a +report indicates the buffer usage is 100%, a warning will be issued.
        +
        Parameters
        prefs{bufferUsageReportingInterval: number, enableNetwork: boolean, enablePage: boolean, enableTimeline: boolean, tracingCategories: string}

        The performance +logging preferences.

        +
        Returns
        Options

        A self reference.

        +

        setProxy(proxy)code »

        Sets the proxy settings for the new session.

        +
        Parameters
        proxyselenium-webdriver.ProxyConfig

        The proxy configuration to use.

        +
        Returns
        Options

        A self reference.

        +

        setUserPreferences(prefs)code »

        Sets the user preferences for Chrome's user profile. See the "Preferences" +file in Chrome's user data directory for examples.

        +
        Parameters
        prefsObject

        Dictionary of user preferences to use.

        +
        Returns
        Options

        A self reference.

        +

        toCapabilities(opt_capabilities)code »

        Converts this options instance to a webdriver.Capabilities object.

        +
        Parameters
        opt_capabilities?Capabilities=

        The capabilities to merge +these options into, if any.

        +
        Returns
        webdriver.Capabilities

        The capabilities.

        +

        Static Functions

        Options.fromCapabilities(capabilities)code »

        Extracts the ChromeDriver specific options from the given capabilities +object.

        +
        Parameters
        capabilitiesCapabilities

        The capabilities object.

        +
        Returns
        Options

        The ChromeDriver options.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_chrome_class_ServiceBuilder.html b/docs/module_selenium-webdriver_chrome_class_ServiceBuilder.html index 3d478af..fad8b45 100644 --- a/docs/module_selenium-webdriver_chrome_class_ServiceBuilder.html +++ b/docs/module_selenium-webdriver_chrome_class_ServiceBuilder.html @@ -1,13 +1,46 @@ -ServiceBuilder

        Class ServiceBuilder

        code »

        Creates remote.DriverService instances that manage a ChromeDriver - server.

        Constructor

        ServiceBuilder ( opt_exe )
        Parameters
        opt_exe: string=
        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.
        Show:

        Instance Methods

        code »build ( )remote.DriverService

        Creates a new DriverService using this instance's current configuration.

        Returns
        A new driver service using this instance's - current configuration.
        Throws
        Error
        If the driver exectuable was not specified and a default - could not be found on the current PATH.
        code »enableVerboseLogging ( )!ServiceBuilder

        Enables verbose logging.

        Returns
        A self reference.
        code »loggingTo ( path )!ServiceBuilder

        Sets the path of the log file the driver should log to. If a log file is - not specified, the driver will log to stderr.

        Parameters
        path: string
        Path of the log file to use.
        Returns
        A self reference.
        code »setNumHttpThreads ( n )!ServiceBuilder

        Sets the number of threads the driver should use to manage HTTP requests. - By default, the driver will use 4 threads.

        Parameters
        n: number
        The number of threads to use.
        Returns
        A self reference.
        code »setStdio ( config )!ServiceBuilder

        Defines the stdio configuration for the driver service. See - child_process.spawn for more information.

        Parameters
        config: (string|!Array)
        The - configuration to use.
        Returns
        A self reference.
        code »setUrlBasePath ( path )!ServiceBuilder

        Sets the base path for WebDriver REST commands (e.g. "/wd/hub"). - By default, the driver will accept commands relative to "/".

        Parameters
        path: string
        The base path to use.
        Returns
        A self reference.
        code »usingPort ( port )!ServiceBuilder

        Sets the port to start the ChromeDriver on.

        Parameters
        port: number
        The port to use, or 0 for any free port.
        Returns
        A self reference.
        Throws
        Error
        If the port is invalid.
        code »withEnvironment ( env )!ServiceBuilder

        Defines the environment to start the server under. This settings will be - inherited by every browser session started by the server.

        Parameters
        env: !Object.<string, string>
        The environment to use.
        Returns
        A self reference.

        Instance Properties

        \ No newline at end of file +ServiceBuilder

        class ServiceBuilder

        Creates selenium-webdriver/remote.DriverService instances that manage +a ChromeDriver +server in a child process.

        +

        new ServiceBuilder(opt_exe)

        Parameters
        opt_exestring=

        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.

        +

        Instance Methods

        build()code »

        Creates a new DriverService using this instance's current configuration.

        +
        Returns
        DriverService

        A new driver service using this instance's +current configuration.

        +
        Throws
        Error

        If the driver exectuable was not specified and a default +could not be found on the current PATH.

        +

        enableVerboseLogging()code »

        Enables verbose logging.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        loggingTo(path)code »

        Sets the path of the log file the driver should log to. If a log file is +not specified, the driver will log to stderr.

        +
        Parameters
        pathstring

        Path of the log file to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        setAdbPort(port)code »

        Sets which port adb is listening to. The ChromeDriver will connect to adb +if an Android session is requested, but +adb must be started beforehand.

        +
        Parameters
        portnumber

        Which port adb is running on.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        setNumHttpThreads(n)code »

        Sets the number of threads the driver should use to manage HTTP requests. +By default, the driver will use 4 threads.

        +
        Parameters
        nnumber

        The number of threads to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        setStdio(config)code »

        Defines the stdio configuration for the driver service. See +child_process.spawn for more information.

        +
        Parameters
        config(string|Array<?(string|number|Stream)>)

        The +configuration to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        setUrlBasePath(path)code »

        Sets the base path for WebDriver REST commands (e.g. "/wd/hub"). +By default, the driver will accept commands relative to "/".

        +
        Parameters
        pathstring

        The base path to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        usingPort(port)code »

        Sets the port to start the ChromeDriver on.

        +
        Parameters
        portnumber

        The port to use, or 0 for any free port.

        +
        Returns
        ServiceBuilder

        A self reference.

        +
        Throws
        Error

        If the port is invalid.

        +

        withEnvironment(env)code »

        Defines the environment to start the server under. This settings will be +inherited by every browser session started by the server.

        +
        Parameters
        envObject<string, string>

        The environment to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_ActionSequence.html b/docs/module_selenium-webdriver_class_ActionSequence.html new file mode 100644 index 0000000..b1f433b --- /dev/null +++ b/docs/module_selenium-webdriver_class_ActionSequence.html @@ -0,0 +1,115 @@ +ActionSequence

        class ActionSequence

        Alias for webdriver.ActionSequence

        Class for defining sequences of complex user interactions. Each sequence +will not be executed until #perform is called.

        +

        Example:

        +
        new webdriver.ActionSequence(driver).
        +    keyDown(webdriver.Key.SHIFT).
        +    click(element1).
        +    click(element2).
        +    dragAndDrop(element3, element4).
        +    keyUp(webdriver.Key.SHIFT).
        +    perform();
        +
        +

        new ActionSequence(driver)

        Parameters
        driverwebdriver.WebDriver

        The driver instance to use.

        +

        Instance Methods

        click(opt_elementOrButton, opt_button)code »

        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:

        +
        sequence.mouseMove(element).click()
        +
        +
        Parameters
        opt_elementOrButton?(webdriver.WebElement|number)=

        Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

        +
        opt_buttonnumber=

        The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +

        doubleClick(opt_elementOrButton, opt_button)code »

        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:

        +
        sequence.mouseMove(element).doubleClick()
        +
        +

        Warning: this method currently only supports the left mouse button. See +issue 4047.

        +
        Parameters
        opt_elementOrButton?(webdriver.WebElement|number)=

        Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

        +
        opt_buttonnumber=

        The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +

        dragAndDrop(element, location)code »

        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).

        +
        Parameters
        elementwebdriver.WebElement

        The element to drag.

        +
        location(webdriver.WebElement|{x: number, y: number})

        The +location to drag to, either as another WebElement or an offset in pixels.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +

        keyDown(key)code »

        Performs a modifier key press. The modifier key is not released +until #keyUp or #sendKeys is called. The key press will be +targetted at the currently focused element.

        +
        Parameters
        keystring

        The modifier key to push. Must be one of +{ALT, CONTROL, SHIFT, COMMAND, META}.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +
        Throws
        Error

        If the key is not a valid modifier key.

        +

        keyUp(key)code »

        Performs a modifier key release. The release is targetted at the currently +focused element.

        +
        Parameters
        keystring

        The modifier key to release. Must be one of +{ALT, CONTROL, SHIFT, COMMAND, META}.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +
        Throws
        Error

        If the key is not a valid modifier key.

        +

        mouseDown(opt_elementOrButton, opt_button)code »

        Presses a mouse button. The mouse button will not be released until +#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.

        +

        If an element is provided, the mouse will first be moved to the center +of that element. This is equivalent to:

        +
        sequence.mouseMove(element).mouseDown()
        +
        +

        Warning: this method currently only supports the left mouse button. See +issue 4047.

        +
        Parameters
        opt_elementOrButton?(webdriver.WebElement|number)=

        Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

        +
        opt_buttonnumber=

        The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +

        mouseMove(location, opt_offset)code »

        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).

        +
        Parameters
        location(webdriver.WebElement|{x: number, y: number})

        The +location to drag to, as either another WebElement or an offset in pixels.

        +
        opt_offset{x: number, y: number}=

        If the target 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
        webdriver.ActionSequence

        A self reference.

        +

        mouseUp(opt_elementOrButton, opt_button)code »

        Releases a mouse button. Behavior is undefined for calling this function +without a previous call to #mouseDown.

        +

        If an element is provided, the mouse will first be moved to the center +of that element. This is equivalent to:

        +
        sequence.mouseMove(element).mouseUp()
        +
        +

        Warning: this method currently only supports the left mouse button. See +issue 4047.

        +
        Parameters
        opt_elementOrButton?(webdriver.WebElement|number)=

        Either +the element to interact with or the button to click with. +Defaults to webdriver.Button.LEFT if neither an element nor +button is specified.

        +
        opt_buttonnumber=

        The button to use. Defaults to +webdriver.Button.LEFT. Ignored if a button is provided as the +first argument.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +

        perform()code »

        Executes this action sequence.

        +
        Returns
        webdriver.promise.Promise

        A promise that will be resolved once +this sequence has completed.

        +

        sendKeys(var_args)code »

        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.

        +
        Parameters
        var_args...(string|Array<string>)

        The keys to type.

        +
        Returns
        webdriver.ActionSequence

        A self reference.

        +
        Throws
        Error

        If the key is not a valid modifier key.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_Builder.html b/docs/module_selenium-webdriver_class_Builder.html new file mode 100644 index 0000000..024bc6e --- /dev/null +++ b/docs/module_selenium-webdriver_class_Builder.html @@ -0,0 +1,138 @@ +Builder

        class Builder

        Alias for module(selenium-webdriver/builder).Builder

        Creates new WebDriver instances. The environment +variables listed below may be used to override a builder's configuration, +allowing quick runtime changes.

        +
        • +

          SELENIUM_BROWSER: defines the target browser in the form +browser[:version][:platform].

          +
        • +

          SELENIUM_REMOTE_URL: defines the remote URL for all builder +instances. This environment variable should be set to a fully qualified +URL for a WebDriver server (e.g. http://localhost:4444/wd/hub). This +option always takes precedence over SELENIUM_SERVER_JAR.

          +
        • +

          SELENIUM_SERVER_JAR: defines the path to the + +standalone Selenium server jar to use. The server will be started the +first time a WebDriver instance and be killed when the process exits.

          +
        +

        Suppose you had mytest.js that created WebDriver with

        +
        var driver = new webdriver.Builder()
        +    .forBrowser('chrome')
        +    .build();
        +
        +

        This test could be made to use Firefox on the local machine by running with +SELENIUM_BROWSER=firefox node mytest.js. Rather than change the code to +target Google Chrome on a remote machine, you can simply set the +SELENIUM_BROWSER and SELENIUM_REMOTE_URL environment variables:

        +
        SELENIUM_BROWSER=chrome:36:LINUX \
        +SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \
        +node mytest.js
        +
        +

        You could also use a local copy of the standalone Selenium server:

        +
        SELENIUM_BROWSER=chrome:36:LINUX \
        +SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \
        +node mytest.js
        +
        +

        new Builder()

        Parameters
        None.

        Instance Methods

        build()code »

        Creates a new WebDriver client based on this builder's current +configuration.

        +
        Returns
        webdriver.WebDriver

        A new WebDriver instance.

        +
        Throws
        Error

        If the current configuration is invalid.

        +

        disableEnvironmentOverrides()code »

        Configures this builder to ignore any environment variable overrides and to +only use the configuration specified through this instance's API.

        +
        Returns
        Builder

        A self reference.

        +

        forBrowser(name, opt_version, opt_platform)code »

        Configures the target browser for clients created by this instance. +Any calls to #withCapabilities after this function will +overwrite these settings.

        +

        You may also define the target browser using the SELENIUM_BROWSER +environment variable. If set, this environment variable should be of the +form browser[:[version][:platform]].

        +
        Parameters
        namestring

        The name of the target browser; +common defaults are available on the webdriver.Browser enum.

        +
        opt_versionstring=

        A desired version; may be omitted if any +version should be used.

        +
        opt_platformstring=

        The desired platform; may be omitted if any +version may be used.

        +
        Returns
        Builder

        A self reference.

        +

        getCapabilities()code »

        Returns the base set of capabilities this instance is currently configured +to use.

        +
        Returns
        webdriver.Capabilities

        The current capabilities for this builder.

        +

        getServerUrl()code »

        Returns
        string

        The URL of the WebDriver server this instance is configured +to use.

        +

        getWebDriverProxy()code »

        Returns
        string

        The URL of the proxy server to use for the WebDriver's HTTP +connections.

        +

        setAlertBehavior(beahvior)code »

        Sets the default action to take with an unexpected alert before returning +an error.

        +
        Parameters
        beahviorstring

        The desired behavior; should be "accept", "dismiss", +or "ignore". Defaults to "dismiss".

        +
        Returns
        Builder

        A self reference.

        +

        setChromeOptions(options)code »

        Sets Chrome specific options +for drivers created by this builder. Any logging or proxy settings defined +on the given options will take precedence over those set through +#setLoggingPrefs and #setProxy, respectively.

        +
        Parameters
        optionschrome.Options

        The ChromeDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setControlFlow(flow)code »

        Sets the control flow that created drivers should execute actions in. If +the flow is never set, or is set to null, it will use the active +flow at the time #build() is called.

        +
        Parameters
        flowwebdriver.promise.ControlFlow

        The control flow to use, or +null to

        +
        Returns
        Builder

        A self reference.

        +

        setEnableNativeEvents(enabled)code »

        Sets whether native events should be used.

        +
        Parameters
        enabledboolean

        Whether to enable native events.

        +
        Returns
        Builder

        A self reference.

        +

        setFirefoxOptions(options)code »

        Sets Firefox specific options +for drivers created by this builder. Any logging or proxy settings defined +on the given options will take precedence over those set through +#setLoggingPrefs and #setProxy, respectively.

        +
        Parameters
        optionsfirefox.Options

        The FirefoxDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setIeOptions(options)code »

        Sets Internet Explorer specific +options for drivers created by +this builder. Any proxy settings defined on the given options will take +precedence over those set through #setProxy.

        +
        Parameters
        optionsie.Options

        The IEDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setLoggingPrefs(prefs)code »

        Sets the logging preferences for the created session. Preferences may be +changed by repeated calls, or by calling #withCapabilities.

        +
        Parameters
        prefs(webdriver.logging.Preferences|Object<string, string>)

        The +desired logging preferences.

        +
        Returns
        Builder

        A self reference.

        +

        setOperaOptions(options)code »

        Sets Opera specific options for +drivers created by this builder. Any logging or proxy settings defined on the +given options will take precedence over those set through +#setLoggingPrefs and #setProxy, respectively.

        +
        Parameters
        optionsopera.Options

        The OperaDriver options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setProxy(config)code »

        Sets the proxy configuration to use for WebDriver clients created by this +builder. Any calls to #withCapabilities after this function will +overwrite these settings.

        +
        Parameters
        config{proxyType: string}

        The configuration to use.

        +
        Returns
        Builder

        A self reference.

        +

        setSafariOptions(options)code »

        Sets Safari specific options +for drivers created by this builder. Any logging settings defined on the +given options will take precedence over those set through +#setLoggingPrefs.

        +
        Parameters
        optionssafari.Options

        The Safari options to use.

        +
        Returns
        Builder

        A self reference.

        +

        setScrollBehavior(behavior)code »

        Sets how elements should be scrolled into view for interaction.

        +
        Parameters
        behaviornumber

        The desired scroll behavior: either 0 to align with +the top of the viewport or 1 to align with the bottom.

        +
        Returns
        Builder

        A self reference.

        +

        usingServer(url)code »

        Sets the URL of a remote WebDriver server to use. Once a remote URL has been +specified, the builder direct all new clients to that server. If this method +is never called, the Builder will attempt to create all clients locally.

        +

        As an alternative to this method, you may also set the SELENIUM_REMOTE_URL +environment variable.

        +
        Parameters
        urlstring

        The URL of a remote server to use.

        +
        Returns
        Builder

        A self reference.

        +

        usingWebDriverProxy(proxy)code »

        Sets the URL of the proxy to use for the WebDriver's HTTP connections. +If this method is never called, the Builder will create a connection without +a proxy.

        +
        Parameters
        proxystring

        The URL of a proxy to use.

        +
        Returns
        Builder

        A self reference.

        +

        withCapabilities(capabilities)code »

        Sets the desired capabilities when requesting a new session. This will +overwrite any previously set capabilities.

        +
        Parameters
        capabilitiesObject

        The desired +capabilities for a new session.

        +
        Returns
        Builder

        A self reference.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_Capabilities.html b/docs/module_selenium-webdriver_class_Capabilities.html new file mode 100644 index 0000000..2c78d90 --- /dev/null +++ b/docs/module_selenium-webdriver_class_Capabilities.html @@ -0,0 +1,60 @@ +Capabilities

        class Capabilities

        webdriver.Serializable<Object<string, ?>>
        +  └ Capabilities
        Alias for webdriver.Capabilities

        new Capabilities(opt_other)

        Parameters
        opt_other?Object=

        Another set of +capabilities to merge into this instance.

        +

        Instance Methods

        get(key)code »

        Parameters
        keystring

        The capability to return.

        +
        Returns

        The capability with the given key, or null if it has +not been set.

        +

        has(key)code »

        Parameters
        keystring

        The capability to check.

        +
        Returns
        boolean

        Whether the specified capability is set.

        +

        merge(other)code »

        Merges another set of capabilities into this instance. Any duplicates in +the provided set will override those already set on this instance.

        +
        Parameters
        otherObject

        The capabilities to +merge into this instance.

        +
        Returns
        webdriver.Capabilities

        A self reference.

        +

        serialize()code »

        Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

        +

        Overrides: webdriver.Serializable

        Returns
        Object<string, ?>

        The JSON representation of this instance. Note, +the returned object may contain nested promises that are promised values.

        +

        set(key, value)code »

        Parameters
        keystring

        The capability to set.

        +
        value*

        The capability value. Capability values must be JSON +serializable. Pass null to unset the capability.

        +
        Returns
        webdriver.Capabilities

        A self reference.

        +

        setAlertBehavior(behavior)code »

        Sets the default action to take with an unexpected alert before returning +an error.

        +
        Parameters
        behaviorstring

        The desired behavior; should be "accept", "dismiss", +or "ignore". Defaults to "dismiss".

        +
        Returns
        webdriver.Capabilities

        A self reference.

        +

        setEnableNativeEvents(enabled)code »

        Sets whether native events should be used.

        +
        Parameters
        enabledboolean

        Whether to enable native events.

        +
        Returns
        webdriver.Capabilities

        A self reference.

        +

        setLoggingPrefs(prefs)code »

        Sets the logging preferences. Preferences may be specified as a +webdriver.logging.Preferences instance, or a as a map of log-type to +log-level.

        +
        Parameters
        prefs(webdriver.logging.Preferences|Object<string, string>)

        The +logging preferences.

        +
        Returns
        webdriver.Capabilities

        A self reference.

        +

        setProxy(proxy)code »

        Sets the proxy configuration for this instance.

        +
        Parameters
        proxy{proxyType: string}

        The desired proxy configuration.

        +
        Returns
        webdriver.Capabilities

        A self reference.

        +

        setScrollBehavior(behavior)code »

        Sets how elements should be scrolled into view for interaction.

        +
        Parameters
        behaviornumber

        The desired scroll behavior: either 0 to align with +the top of the viewport or 1 to align with the bottom.

        +
        Returns
        webdriver.Capabilities

        A self reference.

        +

        Static Functions

        Capabilities.android()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for Android.

        +

        Capabilities.chrome()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for Chrome.

        +

        Capabilities.firefox()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for Firefox.

        +

        Capabilities.htmlunit()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for HTMLUnit.

        +

        Capabilities.htmlunitwithjs()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for HTMLUnit +with enabled Javascript.

        +

        Capabilities.ie()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for +Internet Explorer.

        +

        Capabilities.ipad()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for iPad.

        +

        Capabilities.iphone()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for iPhone.

        +

        Capabilities.opera()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for Opera.

        +

        Capabilities.phantomjs()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for +PhantomJS.

        +

        Capabilities.safari()code »

        Returns
        webdriver.Capabilities

        A basic set of capabilities for Safari.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_Command.html b/docs/module_selenium-webdriver_class_Command.html new file mode 100644 index 0000000..8a50c03 --- /dev/null +++ b/docs/module_selenium-webdriver_class_Command.html @@ -0,0 +1,15 @@ +Command

        class Command

        Alias for webdriver.Command

        Describes a command to be executed by the WebDriverJS framework.

        +

        new Command(name)

        Parameters
        namestring

        The name of this command.

        +

        Instance Methods

        getName()code »

        Returns
        string

        This command's name.

        +

        getParameter(key)code »

        Returns a named command parameter.

        +
        Parameters
        keystring

        The parameter key to look up.

        +
        Returns

        The parameter value, or undefined if it has not been set.

        +

        getParameters()code »

        Returns
        Object<?, *>

        The parameters to send with this command.

        +

        setParameter(name, value)code »

        Sets a parameter to send with this command.

        +
        Parameters
        namestring

        The parameter name.

        +
        value*

        The parameter value.

        +
        Returns
        webdriver.Command

        A self reference.

        +

        setParameters(parameters)code »

        Sets the parameters for this command.

        +
        Parameters
        parametersObject<?, *>

        The command parameters.

        +
        Returns
        webdriver.Command

        A self reference.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_EventEmitter.html b/docs/module_selenium-webdriver_class_EventEmitter.html new file mode 100644 index 0000000..57f15d6 --- /dev/null +++ b/docs/module_selenium-webdriver_class_EventEmitter.html @@ -0,0 +1,35 @@ +EventEmitter

        class EventEmitter

        Alias for webdriver.EventEmitter

        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.

        +

        new EventEmitter()

        Parameters
        None.

        Instance Methods

        addListener(type, listenerFn, opt_scope)code »

        Registers a listener.

        +
        Parameters
        typestring

        The type of event to listen for.

        +
        listenerFnFunction

        The function to invoke when the event is fired.

        +
        opt_scope?Object=

        The object in whose scope to invoke the listener.

        +
        Returns
        webdriver.EventEmitter

        A self reference.

        +

        emit(type, var_args)code »

        Fires an event and calls all listeners.

        +
        Parameters
        typestring

        The type of event to emit.

        +
        var_args...*

        Any arguments to pass to each listener.

        +

        listeners(type)code »

        Returns a mutable list of listeners for a specific type of event.

        +
        Parameters
        typestring

        The type of event to retrieve the listeners for.

        +
        Returns
        Array<{fn: Function, oneshot: boolean, scope: ?(Object)}>

        The registered listeners for +the given event type.

        +

        on(type, listenerFn, opt_scope)code »

        An alias for #addListener().

        +
        Parameters
        typestring

        The type of event to listen for.

        +
        listenerFnFunction

        The function to invoke when the event is fired.

        +
        opt_scope?Object=

        The object in whose scope to invoke the listener.

        +
        Returns
        webdriver.EventEmitter

        A self reference.

        +

        once(type, listenerFn, opt_scope)code »

        Registers a one-time listener which will be called only the first time an +event is emitted, after which it will be removed.

        +
        Parameters
        typestring

        The type of event to listen for.

        +
        listenerFnFunction

        The function to invoke when the event is fired.

        +
        opt_scope?Object=

        The object in whose scope to invoke the listener.

        +
        Returns
        webdriver.EventEmitter

        A self reference.

        +

        removeAllListeners(opt_type)code »

        Removes all listeners for a specific type of event. If no event is +specified, all listeners across all types will be removed.

        +
        Parameters
        opt_typestring=

        The type of event to remove listeners from.

        +
        Returns
        webdriver.EventEmitter

        A self reference.

        +

        removeListener(type, listenerFn)code »

        Removes a previously registered event listener.

        +
        Parameters
        typestring

        The type of event to unregister.

        +
        listenerFnFunction

        The handler function to remove.

        +
        Returns
        webdriver.EventEmitter

        A self reference.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_FileDetector.html b/docs/module_selenium-webdriver_class_FileDetector.html new file mode 100644 index 0000000..e9c4fe4 --- /dev/null +++ b/docs/module_selenium-webdriver_class_FileDetector.html @@ -0,0 +1,21 @@ +FileDetector

        class FileDetector

        Alias for webdriver.FileDetector

        Used with WebElement#sendKeys on file +input elements (<input type="file">) to detect when the entered key +sequence defines the path to a file.

        +

        By default, WebElement's will enter all +key sequences exactly as entered. You may set a +file detector on the parent +WebDriver instance to define custom behavior for handling file elements. Of +particular note is the selenium-webdriver/remote.FileDetector, which +should be used when running against a remote +Selenium Server.

        +

        new FileDetector()

        Parameters
        None.

        Instance Methods

        handleFile(driver, path)code »

        Handles the file specified by the given path, preparing it for use with +the current browser. If the path does not refer to a valid file, it will +be returned unchanged, otherwisee a path suitable for use with the current +browser will be returned.

        +

        This default implementation is a no-op. Subtypes may override this +function for custom tailored file handling.

        +
        Parameters
        driverwebdriver.WebDriver

        The driver for the current browser.

        +
        pathstring

        The path to process.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise for the processed +file path.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_Serializable.html b/docs/module_selenium-webdriver_class_Serializable.html new file mode 100644 index 0000000..b32c21f --- /dev/null +++ b/docs/module_selenium-webdriver_class_Serializable.html @@ -0,0 +1,9 @@ +Serializable

        class Serializable<T>

        Alias for webdriver.Serializable

        Defines an object that can be asynchronously serialized to its WebDriver +wire representation.

        +

        new Serializable()

        Parameters
        None.

        Instance Methods

        serialize()code »

        Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

        +
        Returns
        (T|IThenable<T>)

        This instance's serialized wire format.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_Session.html b/docs/module_selenium-webdriver_class_Session.html new file mode 100644 index 0000000..60f2a3d --- /dev/null +++ b/docs/module_selenium-webdriver_class_Session.html @@ -0,0 +1,13 @@ +Session

        class Session

        Alias for webdriver.Session

        Contains information about a WebDriver session.

        +

        new Session(id, capabilities)

        Parameters
        idstring

        The session ID.

        +
        capabilitiesObject

        The session +capabilities.

        +

        Instance Methods

        getCapabilities()code »

        Returns
        webdriver.Capabilities

        This session's capabilities.

        +

        getCapability(key)code »

        Retrieves the value of a specific capability.

        +
        Parameters
        keystring

        The capability to retrieve.

        +
        Returns

        The capability value.

        +

        getId()code »

        Returns
        string

        This session's ID.

        +

        toJSON(arg0)code »

        Returns the JSON representation of this object, which is just the string +session ID.

        +
        Parameters
        arg0string=
        Returns
        string

        The JSON representation of this Session.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_WebDriver.html b/docs/module_selenium-webdriver_class_WebDriver.html new file mode 100644 index 0000000..99951bc --- /dev/null +++ b/docs/module_selenium-webdriver_class_WebDriver.html @@ -0,0 +1,313 @@ +WebDriver

        class WebDriver

        Alias for webdriver.WebDriver

        Creates a new WebDriver client, which provides control over a browser.

        +

        Every WebDriver command returns a 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'));
        +});
        +
        +

        new WebDriver(session, executor, opt_flow)

        Parameters
        session(webdriver.Session|webdriver.promise.Promise)

        Either a +known session or a promise that will be resolved to a session.

        +
        executorwebdriver.CommandExecutor

        The executor to use when +sending commands to the browser.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The flow to +schedule commands through. Defaults to the active flow object.

        +

        Instance Methods

        actions()code »

        Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

        +
        driver.actions().
        +    mouseDown(element1).
        +    mouseMove(element2).
        +    mouseUp().
        +    perform();
        +
        +
        Returns
        webdriver.ActionSequence

        A new action sequence for this instance.

        +

        <T> call(fn, opt_scope, var_args)code »

        Schedules a command to execute a custom function.

        +
        Parameters
        fnfunction(...?): (T|webdriver.promise.Promise<T>)

        The function to +execute.

        +
        opt_scope?Object=

        The object in whose scope to execute the function.

        +
        var_args...*

        Any arguments to pass to the function.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved' +with the function's result.

        +

        close()code »

        Schedules a command to close the current window.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when this command has completed.

        +

        controlFlow()code »

        Returns
        webdriver.promise.ControlFlow

        The control flow used by this +instance.

        +

        <T> executeAsyncScript(script, var_args)code »

        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 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 #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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Example #1: Performing a sleep that is synchronized with the currently +selected window:

        +
        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 #2: Synchronizing a test with an AJAX application:

        +
        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');
        +
        +

        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.

        +
        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.responseText);
        +    }
        +  }
        +  xhr.send('');
        +}).then(function(str) {
        +  console.log(JSON.parse(str)['food']);
        +});
        +
        +
        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        <T> executeScript(script, var_args)code »

        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 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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +
        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        findElement(locator)code »

        Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator to use.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedule a command to search for multiple elements on the page.

        +
        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        get(url)code »

        Schedules a command to navigate to the given URL.

        +
        Parameters
        urlstring

        The fully qualified URL to open.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the document has finished loading.

        +

        getAllWindowHandles()code »

        Schedules a command to retrieve the current list of available window handles.

        +
        Returns
        webdriver.promise.Promise<Array<string>>

        A promise that will +be resolved with an array of window handles.

        +

        getCapabilities()code »

        Returns
        webdriver.promise.Promise<webdriver.Capabilities>

        A promise +that will resolve with the this instance's capabilities.

        +

        getCurrentUrl()code »

        Schedules a command to retrieve the URL of the current page.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current URL.

        +

        getPageSource()code »

        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.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page source.

        +

        getSession()code »

        Returns
        webdriver.promise.Promise<webdriver.Session>

        A promise for this +client's session.

        +

        getTitle()code »

        Schedules a command to retrieve the current page's title.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page's title.

        +

        getWindowHandle()code »

        Schedules a command to retrieve they current window handle.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current window handle.

        +

        isElementPresent(locatorOrElement)code »

        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.

        +
        Parameters
        locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator to use, or the actual +DOM element to be located by the server.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the element is present on the page.

        +

        manage()code »

        Returns
        webdriver.WebDriver.Options

        The options interface for this +instance.

        +


        quit()code »

        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.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the command has completed.

        +

        <T> schedule(command, description)code »

        Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

        +
        Parameters
        commandwebdriver.Command

        The command to schedule.

        +
        descriptionstring

        A description of the command for debugging.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved +with the command result.

        +

        setFileDetector(detector)code »

        Sets the file detector that should be +used with this instance.

        +
        Parameters
        detectorwebdriver.FileDetector

        The detector to use or null.

        +

        sleep(ms)code »

        Schedules a command to make the driver sleep for the given amount of time.

        +
        Parameters
        msnumber

        The amount of time, in milliseconds, to sleep.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the sleep has finished.

        +

        switchTo()code »

        Returns
        webdriver.WebDriver.TargetLocator

        The target locator interface for +this instance.

        +

        takeScreenshot()code »

        Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

        +
        1. Entire page +
        2. Current window +
        3. Visible portion of the current frame +
        4. The screenshot of the entire display containing the browser +
        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

        +

        touchActions()code »

        Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

        +
        driver.touchActions().
        +    tap(element1).
        +    doubleTap(element2).
        +    perform();
        +
        +
        Returns
        webdriver.TouchSequence

        A new touch sequence for this instance.

        +

        <T> wait(condition, opt_timeout, opt_message)code »

        Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

        +

        For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

        +

        Example: waiting up to 10 seconds for an element to be present and visible +on the page.

        +
        var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        +button.click();
        +
        +

        This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

        +

        Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

        +
        var started = startTestServer();
        +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        +driver.get(getServerUrl());
        +
        +
        Parameters
        condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

        The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

        +
        opt_timeoutnumber=

        How long to wait for the condition to be true.

        +
        opt_messagestring=

        An optional message to use if the wait times +out.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

        +

        Static Functions

        WebDriver.attachToSession(executor, sessionId, opt_flow)code »

        Creates a new WebDriver client for an existing session.

        +
        Parameters
        executorwebdriver.CommandExecutor

        Command executor to use when +querying for session details.

        +
        sessionIdstring

        ID of the session to attach to.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The control flow all driver +commands should execute under. Defaults to the +currently active control flow.

        +
        Returns
        webdriver.WebDriver

        A new client for the specified session.

        +

        WebDriver.createSession(executor, desiredCapabilities, opt_flow)code »

        Creates a new WebDriver session.

        +
        Parameters
        executorwebdriver.CommandExecutor

        The executor to create the new +session with.

        +
        desiredCapabilitieswebdriver.Capabilities

        The desired +capabilities for the new session.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The control flow all driver +commands should execute under, including the initial session creation. +Defaults to the currently active +control flow.

        +
        Returns
        webdriver.WebDriver

        The driver for the newly created session.

        +

        Types

        WebDriver.Logs

        Interface for managing WebDriver log records.

        +
        WebDriver.Navigation

        Interface for navigating back and forth in the browser history.

        +
        WebDriver.Options

        Provides methods for managing browser and driver state.

        +
        WebDriver.TargetLocator

        An interface for changing the focus of the driver to another frame or window.

        +
        WebDriver.Timeouts

        An interface for managing timeout behavior for WebDriver instances.

        +
        WebDriver.Window

        An interface for managing the current window.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_WebElement.html b/docs/module_selenium-webdriver_class_WebElement.html new file mode 100644 index 0000000..9d195a1 --- /dev/null +++ b/docs/module_selenium-webdriver_class_WebElement.html @@ -0,0 +1,216 @@ +WebElement

        class WebElement

        webdriver.Serializable<{ELEMENT: string}>
        +  └ WebElement
        Alias for webdriver.WebElement

        Represents a DOM element. WebElements can be found by searching from the +document root using a webdriver.WebDriver instance, or by searching +under another WebElement:

        +
        driver.get('http://www.google.com');
        +var searchForm = driver.findElement(By.tagName('form'));
        +var searchBox = searchForm.findElement(By.name('q'));
        +searchBox.sendKeys('webdriver');
        +
        +

        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.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');
        +});
        +
        +

        new WebElement(driver, id)

        Parameters
        driverwebdriver.WebDriver

        The parent WebDriver instance for this +element.

        +
        id(webdriver.promise.Promise<{ELEMENT: string}>|{ELEMENT: string})

        The server-assigned opaque ID for the +underlying DOM element.

        +

        Instance Methods

        clear()code »

        Schedules a command to clear the 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
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the element has been cleared.

        +

        click()code »

        Schedules a command to click on this element.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the click command has completed.

        +

        findElement(locator)code »

        Schedule a command to find a descendant of this element. If the element +cannot be found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator strategy to use when searching for the element.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedules a command to find all of the descendants of this element that +match the given search criteria.

        +
        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator strategy to use when searching for the elements.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        getAttribute(attributeName)code »

        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:

        +
        • "class"
        • "readonly"
        +
        Parameters
        attributeNamestring

        The name of the attribute to query.

        +
        Returns
        webdriver.promise.Promise<?string>

        A promise that will be +resolved with the attribute's value. The returned value will always be +either a string or null.

        +

        getCssValue(cssStyleProperty)code »

        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)).

        +

        Warning: the value returned will be as the browser interprets it, so +it may be tricky to form a proper assertion.

        +
        Parameters
        cssStylePropertystring

        The name of the CSS style property to look +up.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the requested CSS value.

        +

        getDriver()code »

        Returns
        webdriver.WebDriver

        The parent driver for this instance.

        +

        getId()code »

        Returns
        webdriver.promise.Promise<{ELEMENT: string}>

        A promise +that resolves to this element's JSON representation as defined by the +WebDriver wire protocol.

        +

        getInnerHtml()code »

        Schedules a command to retrieve the inner HTML of this element.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's inner HTML.

        +

        getLocation()code »

        Schedules a command to compute the location of this element in page space.

        +
        Returns
        webdriver.promise.Promise<{x: number, y: number}>

        A promise that +will be resolved to the element's location as a +{x:number, y:number} object.

        +

        getOuterHtml()code »

        Schedules a command to retrieve the outer HTML of this element.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's outer HTML.

        +

        getRawId()code »

        Returns the raw ID string ID for this element.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that resolves to this +element's raw ID as a string value.

        +

        getSize()code »

        Schedules a command to compute the size of this element's bounding box, in +pixels.

        +
        Returns
        webdriver.promise.Promise<{height: number, width: number}>

        A +promise that will be resolved with the element's size as a +{width:number, height:number} object.

        +

        getTagName()code »

        Schedules a command to query for the tag/node name of this element.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's tag name.

        +

        getText()code »

        Get the visible (i.e. not hidden by CSS) innerText of this element, including +sub-elements, without any leading or trailing whitespace.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's visible text.

        +

        isDisplayed()code »

        Schedules a command to test whether this element is currently displayed.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether this element is currently visible on the page.

        +

        isElementPresent(locator)code »

        Schedules a command to test if there is at least one descendant of this +element that matches the given search criteria.

        +
        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether an element could be located on the page.

        +

        isEnabled()code »

        Schedules a command to query whether the DOM element represented by this +instance is enabled, as dicted by the disabled attribute.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether this element is currently enabled.

        +

        isSelected()code »

        Schedules a command to query whether this element is selected.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether this element is currently selected.

        +

        sendKeys(var_args)code »

        Schedules a command to type a sequence on the DOM element represented by this +instance.

        +

        Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is +processed in the keysequence, that key state is toggled until one of the +following occurs:

        +
        • +

          The modifier key is encountered again in the sequence. At this point the +state of the key is toggled (along with the appropriate keyup/down events).

          +
        • +

          The 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");
          +
          +
        • +

          The end of the keysequence is encountered. When there are no more keys +to type, all depressed modifier keys are released (with accompanying keyup +events).

          +
        +

        If this element is a file input (<input type="file">), the +specified key sequence should specify the path to the file to attach to +the element. This is analgous to the user clicking "Browse..." and entering +the path into the file select dialog.

        +
        var form = driver.findElement(By.css('form'));
        +var element = form.findElement(By.css('input[type=file]'));
        +element.sendKeys('/path/to/file.txt');
        +form.submit();
        +
        +

        For uploads to function correctly, the entered path must reference a file +on the browser's machine, not the local machine running this script. When +running against a remote Selenium server, a webdriver.FileDetector +may be used to transparently copy files to the remote machine before +attempting to upload them in the browser.

        +

        Note: On browsers where native keyboard events are not 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.

        +
        Parameters
        var_args...(string|webdriver.promise.Promise<string>)

        The sequence +of keys to type. All arguments will be joined into a single sequence.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when all keys have been typed.

        +

        serialize()code »

        Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

        +

        Overrides: webdriver.Serializable

        Returns
        (webdriver.WebElement.Id|IThenable<webdriver.WebElement.Id>)

        This instance's serialized wire format.

        +

        submit()code »

        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.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the form has been submitted.

        +

        Static Functions

        WebElement.equals(a, b)code »

        Compares to WebElements for equality.

        +
        Parameters
        awebdriver.WebElement

        A WebElement.

        +
        bwebdriver.WebElement

        A WebElement.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved to whether the two WebElements are equal.

        +

        Static Properties

        WebElement.ELEMENT_KEYstring

        The property key used in the wire protocol to indicate that a JSON object +contains the ID of a WebElement.

        +

        Type Definitions

        WebElement.Id{ELEMENT: string}

        Wire protocol definition of a WebElement ID.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_class_WebElementPromise.html b/docs/module_selenium-webdriver_class_WebElementPromise.html new file mode 100644 index 0000000..9905850 --- /dev/null +++ b/docs/module_selenium-webdriver_class_WebElementPromise.html @@ -0,0 +1,267 @@ +WebElementPromise

        class WebElementPromise

        webdriver.Serializable<{ELEMENT: string}>
        +  └ webdriver.WebElement
        +      └ WebElementPromise
        Alias for webdriver.WebElementPromise
        All implemented interfaces
        IThenable<T>
        webdriver.promise.Thenable<webdriver.WebElement>

        WebElementPromise is a promise that will be fulfilled with a WebElement. +This serves as a forward proxy on WebElement, allowing calls to be +scheduled without directly on this instance before the underlying +WebElement has been fulfilled. In other words, the following two statements +are equivalent:

        +
        driver.findElement({id: 'my-button'}).click();
        +driver.findElement({id: 'my-button'}).then(function(el) {
        +  return el.click();
        +});
        +
        +

        new WebElementPromise(driver, el)

        Parameters
        driverwebdriver.WebDriver

        The parent WebDriver instance for this +element.

        +
        elwebdriver.promise.Promise<webdriver.WebElement>

        A promise +that will resolve to the promised element.

        +

        Instance Methods

        cancel(opt_reason)code »

        Cancels the computation of this promise's value, rejecting the promise in +the process. This method is a no-op if the promise has already been +resolved.

        +

        Specified by: webdriver.promise.Thenable

        Parameters
        opt_reason?(string|webdriver.promise.CancellationError)=

        The reason this +promise is being cancelled.

        +

        clear()code »

        Schedules a command to clear the value of this element. This command +has no effect if the underlying DOM element is neither a text INPUT element +nor a TEXTAREA element.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the element has been cleared.

        +

        click()code »

        Schedules a command to click on this element.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the click command has completed.

        +

        findElement(locator)code »

        Schedule a command to find a descendant of this element. If the element +cannot be found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        Defined by: webdriver.WebElement

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator strategy to use when searching for the element.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedules a command to find all of the descendants of this element that +match the given search criteria.

        +

        Defined by: webdriver.WebElement

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator strategy to use when searching for the elements.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        getAttribute(attributeName)code »

        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:

        +
        • "class"
        • "readonly"
        +

        Defined by: webdriver.WebElement

        Parameters
        attributeNamestring

        The name of the attribute to query.

        +
        Returns
        webdriver.promise.Promise<?string>

        A promise that will be +resolved with the attribute's value. The returned value will always be +either a string or null.

        +

        getCssValue(cssStyleProperty)code »

        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)).

        +

        Warning: the value returned will be as the browser interprets it, so +it may be tricky to form a proper assertion.

        +

        Defined by: webdriver.WebElement

        Parameters
        cssStylePropertystring

        The name of the CSS style property to look +up.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the requested CSS value.

        +

        getDriver()code »

        Defined by: webdriver.WebElement

        Returns
        webdriver.WebDriver

        The parent driver for this instance.

        +

        getId()code »

        Defers returning the element ID until the wrapped WebElement has been +resolved.

        +

        Overrides: webdriver.WebElement

        Returns
        webdriver.promise.Promise<{ELEMENT: string}>

        A promise +that resolves to this element's JSON representation as defined by the +WebDriver wire protocol.

        +

        getInnerHtml()code »

        Schedules a command to retrieve the inner HTML of this element.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's inner HTML.

        +

        getLocation()code »

        Schedules a command to compute the location of this element in page space.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<{x: number, y: number}>

        A promise that +will be resolved to the element's location as a +{x:number, y:number} object.

        +

        getOuterHtml()code »

        Schedules a command to retrieve the outer HTML of this element.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's outer HTML.

        +

        getRawId()code »

        Returns the raw ID string ID for this element.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<string>

        A promise that resolves to this +element's raw ID as a string value.

        +

        getSize()code »

        Schedules a command to compute the size of this element's bounding box, in +pixels.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<{height: number, width: number}>

        A +promise that will be resolved with the element's size as a +{width:number, height:number} object.

        +

        getTagName()code »

        Schedules a command to query for the tag/node name of this element.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's tag name.

        +

        getText()code »

        Get the visible (i.e. not hidden by CSS) innerText of this element, including +sub-elements, without any leading or trailing whitespace.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the element's visible text.

        +

        isDisplayed()code »

        Schedules a command to test whether this element is currently displayed.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether this element is currently visible on the page.

        +

        isElementPresent(locator)code »

        Schedules a command to test if there is at least one descendant of this +element that matches the given search criteria.

        +

        Defined by: webdriver.WebElement

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether an element could be located on the page.

        +

        isEnabled()code »

        Schedules a command to query whether the DOM element represented by this +instance is enabled, as dicted by the disabled attribute.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether this element is currently enabled.

        +

        isPending()code »

        Specified by: webdriver.promise.Thenable

        Returns
        boolean

        Whether this promise's value is still being computed.

        +

        isSelected()code »

        Schedules a command to query whether this element is selected.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<boolean>

        A promise that will be +resolved with whether this element is currently selected.

        +

        sendKeys(var_args)code »

        Schedules a command to type a sequence on the DOM element represented by this +instance.

        +

        Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is +processed in the keysequence, that key state is toggled until one of the +following occurs:

        +
        • +

          The modifier key is encountered again in the sequence. At this point the +state of the key is toggled (along with the appropriate keyup/down events).

          +
        • +

          The 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");
          +
          +
        • +

          The end of the keysequence is encountered. When there are no more keys +to type, all depressed modifier keys are released (with accompanying keyup +events).

          +
        +

        If this element is a file input (<input type="file">), the +specified key sequence should specify the path to the file to attach to +the element. This is analgous to the user clicking "Browse..." and entering +the path into the file select dialog.

        +
        var form = driver.findElement(By.css('form'));
        +var element = form.findElement(By.css('input[type=file]'));
        +element.sendKeys('/path/to/file.txt');
        +form.submit();
        +
        +

        For uploads to function correctly, the entered path must reference a file +on the browser's machine, not the local machine running this script. When +running against a remote Selenium server, a webdriver.FileDetector +may be used to transparently copy files to the remote machine before +attempting to upload them in the browser.

        +

        Note: On browsers where native keyboard events are not 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.

        +

        Defined by: webdriver.WebElement

        Parameters
        var_args...(string|webdriver.promise.Promise<string>)

        The sequence +of keys to type. All arguments will be joined into a single sequence.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when all keys have been typed.

        +

        serialize()code »

        Returns either this instance's serialized represention, if immediately +available, or a promise for its serialized representation. This function is +conceptually equivalent to objects that have a toJSON() property, +except the serialize() result may be a promise or an object containing a +promise (which are not directly JSON friendly).

        +

        Defined by: webdriver.WebElement
        Overrides: webdriver.Serializable

        Returns
        (webdriver.WebElement.Id|IThenable<webdriver.WebElement.Id>)

        This instance's serialized wire format.

        +

        submit()code »

        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.

        +

        Defined by: webdriver.WebElement

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the form has been submitted.

        +

        then(opt_callback, opt_errback)code »

        Registers listeners for when this instance is resolved.

        +

        Specified by: webdriver.promise.Thenable, IThenable

        Parameters
        opt_callback?function(T): (R|IThenable<R>)=

        The +function to call if this promise is successfully resolved. The function +should expect a single argument: the promise's resolved value.

        +
        opt_errback?function(*): (R|IThenable<R>)=

        The function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

        +
        Returns
        webdriver.promise.Promise

        A new promise which will be +resolved with the result of the invoked callback.

        +

        thenCatch(errback)code »

        Registers a listener for when this promise is rejected. This is synonymous +with the 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);
        +});
        +
        +

        Specified by: webdriver.promise.Thenable

        Parameters
        errbackfunction(*): (R|IThenable<R>)

        The +function to call if this promise is rejected. The function should +expect a single argument: the rejection reason.

        +
        Returns
        webdriver.promise.Promise

        A new promise which will be +resolved with the result of the invoked callback.

        +

        thenFinally(callback)code »

        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 finally clause in a synchronous API:

        +
        // Synchronous API:
        +try {
        +  doSynchronousWork();
        +} finally {
        +  cleanUp();
        +}
        +
        +// Asynchronous promise API:
        +doAsynchronousWork().thenFinally(cleanUp);
        +
        +

        Note: similar to the 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
        +}
        +
        +promise.rejected(Error('one'))
        +    .thenFinally(function() {
        +      throw Error('two');  // Hides Error: one
        +    });
        +
        +

        Specified by: webdriver.promise.Thenable

        Parameters
        callbackfunction(): (R|IThenable<R>)

        The function +to call when this promise is resolved.

        +
        Returns
        webdriver.promise.Promise

        A promise that will be fulfilled +with the callback result.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_enum_Browser.html b/docs/module_selenium-webdriver_enum_Browser.html new file mode 100644 index 0000000..c2dbf58 --- /dev/null +++ b/docs/module_selenium-webdriver_enum_Browser.html @@ -0,0 +1,2 @@ +Browser
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_enum_Button.html b/docs/module_selenium-webdriver_enum_Button.html new file mode 100644 index 0000000..0b0a57d --- /dev/null +++ b/docs/module_selenium-webdriver_enum_Button.html @@ -0,0 +1,2 @@ +Button
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_enum_Capability.html b/docs/module_selenium-webdriver_enum_Capability.html new file mode 100644 index 0000000..889d551 --- /dev/null +++ b/docs/module_selenium-webdriver_enum_Capability.html @@ -0,0 +1,33 @@ +Capability

        enum Capability

        Typestring
        Alias for webdriver.Capability

        Common webdriver capability keys.

        +

        Values and Descriptions

        ACCEPT_SSL_CERTS

        Indicates whether a driver should accept all SSL certs by default. This +capability only applies when requesting a new session. To query whether +a driver can handle insecure SSL certs, see #SECURE_SSL.

        +
        BROWSER_NAME

        The browser name. Common browser names are defined in the +webdriver.Browser enum.

        +
        ELEMENT_SCROLL_BEHAVIOR

        Defines how elements should be scrolled into the viewport for interaction. +This capability will be set to zero (0) if elements are aligned with the +top of the viewport, or one (1) if aligned with the bottom. The default +behavior is to align with the top of the viewport.

        +
        HANDLES_ALERTS

        Whether the driver is capable of handling modal alerts (e.g. alert, +confirm, prompt). To define how a driver should handle alerts, +use #UNEXPECTED_ALERT_BEHAVIOR.

        +
        LOGGING_PREFS

        Key for the logging driver logging preferences.

        +
        NATIVE_EVENTS

        Whether this session generates native events when simulating user input.

        +
        PLATFORM

        Describes the platform the browser is running on. Will be one of +ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When requesting a +session, ANY may be used to indicate no platform preference (this is +semantically equivalent to omitting the platform capability).

        +
        PROXY

        Describes the proxy configuration to use for a new WebDriver session.

        +
        ROTATABLE

        Whether the driver supports changing the brower's orientation.

        +
        SECURE_SSL

        Whether a driver is only capable of handling secure SSL certs. To request +that a driver accept insecure SSL certs by default, use +#ACCEPT_SSL_CERTS.

        +
        SUPPORTS_APPLICATION_CACHE

        Whether the driver supports manipulating the app cache.

        +
        SUPPORTS_CSS_SELECTORS

        Whether the driver supports locating elements with CSS selectors.

        +
        SUPPORTS_JAVASCRIPT

        Whether the browser supports JavaScript.

        +
        SUPPORTS_LOCATION_CONTEXT

        Whether the driver supports controlling the browser's location info.

        +
        TAKES_SCREENSHOT

        Whether the driver supports taking screenshots.

        +
        UNEXPECTED_ALERT_BEHAVIOR

        Defines how the driver should handle unexpected alerts. The value should +be one of "accept", "dismiss", or "ignore.

        +
        VERSION

        Defines the browser version.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_enum_CommandName.html b/docs/module_selenium-webdriver_enum_CommandName.html new file mode 100644 index 0000000..cea56af --- /dev/null +++ b/docs/module_selenium-webdriver_enum_CommandName.html @@ -0,0 +1,3 @@ +CommandName

        enum CommandName

        Typestring
        Alias for webdriver.CommandName

        Enumeration of predefined names command names that all command processors +will support.

        +

        Values and Descriptions

        ACCEPT_ALERT
        CLEAR_APP_CACHE
        CLEAR_ELEMENT
        CLEAR_LOCAL_STORAGE
        CLEAR_SESSION_STORAGE
        CLICK
        CLICK_ELEMENT
        CLOSE
        DELETE_ALL_COOKIES
        DESCRIBE_SESSION
        DISMISS_ALERT
        DOUBLE_CLICK
        ELEMENT_EQUALS
        EXECUTE_ASYNC_SCRIPT
        EXECUTE_SCRIPT
        EXECUTE_SQL
        FIND_CHILD_ELEMENT
        FIND_CHILD_ELEMENTS
        FIND_ELEMENT
        FIND_ELEMENTS
        GET
        GET_ACTIVE_ELEMENT
        GET_ALERT_TEXT
        GET_ALL_COOKIES
        GET_APP_CACHE
        GET_APP_CACHE_STATUS
        GET_AVAILABLE_LOG_TYPES
        GET_CURRENT_URL
        GET_CURRENT_WINDOW_HANDLE
        GET_ELEMENT_ATTRIBUTE
        GET_ELEMENT_LOCATION
        GET_ELEMENT_LOCATION_IN_VIEW
        GET_ELEMENT_SIZE
        GET_ELEMENT_TAG_NAME
        GET_ELEMENT_TEXT
        GET_ELEMENT_VALUE_OF_CSS_PROPERTY
        GET_LOCAL_STORAGE_ITEM
        GET_LOCAL_STORAGE_KEYS
        GET_LOCAL_STORAGE_SIZE
        GET_LOCATION
        GET_LOG
        GET_PAGE_SOURCE
        GET_SCREEN_ORIENTATION
        GET_SERVER_STATUS
        GET_SESSIONS
        GET_SESSION_LOGS
        GET_SESSION_STORAGE_ITEM
        GET_SESSION_STORAGE_KEYS
        GET_SESSION_STORAGE_SIZE
        GET_TITLE
        GET_WINDOW_HANDLES
        GET_WINDOW_POSITION
        GET_WINDOW_SIZE
        GO_BACK
        GO_FORWARD
        IMPLICITLY_WAIT
        IS_BROWSER_ONLINE
        IS_ELEMENT_DISPLAYED
        IS_ELEMENT_ENABLED
        IS_ELEMENT_SELECTED
        MAXIMIZE_WINDOW
        MOUSE_DOWN
        MOUSE_UP
        MOVE_TO
        NEW_SESSION
        QUIT
        REFRESH
        REMOVE_LOCAL_STORAGE_ITEM
        REMOVE_SESSION_STORAGE_ITEM
        SCREENSHOT
        SEND_KEYS_TO_ACTIVE_ELEMENT
        SEND_KEYS_TO_ELEMENT
        SET_ALERT_TEXT
        SET_BROWSER_ONLINE
        SET_LOCAL_STORAGE_ITEM
        SET_LOCATION
        SET_SCREEN_ORIENTATION
        SET_SCRIPT_TIMEOUT
        SET_SESSION_STORAGE_ITEM
        SET_TIMEOUT
        SET_WINDOW_POSITION
        SET_WINDOW_SIZE
        SUBMIT_ELEMENT
        SWITCH_TO_FRAME
        SWITCH_TO_WINDOW
        TOUCH_DOUBLE_TAP
        TOUCH_DOWN
        TOUCH_FLICK
        TOUCH_LONG_PRESS
        TOUCH_MOVE
        TOUCH_SCROLL
        TOUCH_SINGLE_TAP
        TOUCH_UP
        UPLOAD_FILE
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_enum_Key.html b/docs/module_selenium-webdriver_enum_Key.html new file mode 100644 index 0000000..18db2b6 --- /dev/null +++ b/docs/module_selenium-webdriver_enum_Key.html @@ -0,0 +1,12 @@ +Key

        enum Key

        Typestring
        Alias for webdriver.Key

        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

        +

        Values and Descriptions

        ADD
        ALT
        ARROW_DOWN
        ARROW_LEFT
        ARROW_RIGHT
        ARROW_UP
        BACK_SPACE
        CANCEL
        CLEAR
        COMMAND
        CONTROL
        DECIMAL
        DELETE
        DIVIDE
        DOWN
        END
        ENTER
        EQUALS
        ESCAPE
        F1
        F10
        F11
        F12
        F2
        F3
        F4
        F5
        F6
        F7
        F8
        F9
        HELP
        HOME
        INSERT
        LEFT
        META
        MULTIPLY
        NULL
        NUMPAD0
        NUMPAD1
        NUMPAD2
        NUMPAD3
        NUMPAD4
        NUMPAD5
        NUMPAD6
        NUMPAD7
        NUMPAD8
        NUMPAD9
        PAGE_DOWN
        PAGE_UP
        PAUSE
        RETURN
        SEMICOLON
        SEPARATOR
        SHIFT
        SPACE
        SUBTRACT
        TAB
        UP

        Functions

        Key.chord(var_args)code »

        Simulate pressing many keys at once in a "chord". Takes a sequence of +webdriver.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
        var_args...string

        The key sequence to concatenate.

        +
        Returns
        string

        The null-terminated key sequence.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_error.html b/docs/module_selenium-webdriver_error.html index dc4ba90..a8c05a9 100644 --- a/docs/module_selenium-webdriver_error.html +++ b/docs/module_selenium-webdriver_error.html @@ -1,4 +1,4 @@ -selenium-webdriver/error

        Module selenium-webdriver/error

        code »

        Classes

        Error
        Error extension that includes error status codes from the WebDriver wire - protocol: - http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes

        Enumerations

        ErrorCode
        Error codes from the WebDriver wire protocol: - http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
        Show:
        \ No newline at end of file +selenium-webdriver/error

        module selenium-webdriver/error

        Types

        Error

        Represents an error returned from a WebDriver command request.

        +
        ErrorCode

        Error codes from the Selenium WebDriver protocol: +https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#response-status-codes

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_error_class_Error.html b/docs/module_selenium-webdriver_error_class_Error.html new file mode 100644 index 0000000..d8f3d1c --- /dev/null +++ b/docs/module_selenium-webdriver_error_class_Error.html @@ -0,0 +1,15 @@ +Error

        class Error

        Error
        +  └ Error
        Alias for bot.Error

        Represents an error returned from a WebDriver command request.

        +

        new Error(code, opt_message)

        Parameters
        codenumber

        The error's status code.

        +
        opt_messagestring=

        Optional error message.

        +

        Instance Methods

        toString()code »

        Returns
        string

        The string representation of this error.

        +

        Instance Properties

        codenumber

        This error's status code.

        +
        descriptionstring

        IE-only.

        +
        fileNamestring

        Mozilla-only

        +
        isAutomationErrorboolean

        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.

        +
        lineNumbernumber

        Mozilla-only.

        +
        messagestring
        No description.
        namestring
        No description.
        sourceURL?

        Doesn't seem to exist, but closure/debug.js references it.

        +
        stackstring
        No description.
        statestring
        No description.

        Types

        Error.State

        Status strings enumerated in the W3C WebDriver protocol.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_error_enum_ErrorCode.html b/docs/module_selenium-webdriver_error_enum_ErrorCode.html new file mode 100644 index 0000000..55e62ec --- /dev/null +++ b/docs/module_selenium-webdriver_error_enum_ErrorCode.html @@ -0,0 +1,3 @@ +ErrorCode

        enum ErrorCode

        Typenumber
        Alias for bot.ErrorCode

        Error codes from the Selenium WebDriver protocol: +https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#response-status-codes

        +

        Values and Descriptions

        ELEMENT_NOT_SELECTABLE
        ELEMENT_NOT_VISIBLE
        IME_ENGINE_ACTIVATION_FAILED
        IME_NOT_AVAILABLE
        INVALID_ELEMENT_COORDINATES
        INVALID_ELEMENT_STATE
        INVALID_SELECTOR_ERROR
        INVALID_XPATH_SELECTOR
        INVALID_XPATH_SELECTOR_RETURN_TYPE
        JAVASCRIPT_ERROR
        METHOD_NOT_ALLOWED
        MOVE_TARGET_OUT_OF_BOUNDS
        NO_SUCH_ALERT
        NO_SUCH_ELEMENT
        NO_SUCH_FRAME
        NO_SUCH_WINDOW
        SCRIPT_TIMEOUT
        SESSION_NOT_CREATED
        SQL_DATABASE_ERROR
        STALE_ELEMENT_REFERENCE
        SUCCESS
        TIMEOUT
        UNEXPECTED_ALERT_OPEN
        UNKNOWN_COMMAND
        UNKNOWN_ERROR
        UNSUPPORTED_OPERATION
        XPATH_LOOKUP_ERROR
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_executors.html b/docs/module_selenium-webdriver_executors.html index 4cd5307..0f39862 100644 --- a/docs/module_selenium-webdriver_executors.html +++ b/docs/module_selenium-webdriver_executors.html @@ -1,3 +1,10 @@ -selenium-webdriver/executors

        Module selenium-webdriver/executors

        code »

        Various utilities for working with - webdriver.CommandExecutor implementations.

        Show:

        Functions

        Creates a command executor that uses WebDriver's JSON wire protocol.

        Parameters
        url: (string|!webdriver.promise.Promise.<string>)
        The server's URL, - or a promise that will resolve to that URL.
        \ No newline at end of file +selenium-webdriver/executors

        module selenium-webdriver/executors

        Various utilities for working with +webdriver.CommandExecutor implementations.

        +

        Functions

        createExecutor(url, opt_proxy)code »

        Creates a command executor that uses WebDriver's JSON wire protocol.

        +
        Parameters
        url(string|webdriver.promise.Promise<string>)

        The server's URL, +or a promise that will resolve to that URL.

        +
        opt_proxystring=

        (optional) The URL of the HTTP proxy for the +client to use.

        +

        Types

        DeferredExecutor

        Wraps a promised webdriver.CommandExecutor, ensuring no commands +are executed until the wrapped executor has been fully built.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_executors_class_DeferredExecutor.html b/docs/module_selenium-webdriver_executors_class_DeferredExecutor.html new file mode 100644 index 0000000..26ff73b --- /dev/null +++ b/docs/module_selenium-webdriver_executors_class_DeferredExecutor.html @@ -0,0 +1,12 @@ +DeferredExecutor

        class DeferredExecutor

        All implemented interfaces
        webdriver.CommandExecutor

        Wraps a promised webdriver.CommandExecutor, ensuring no commands +are executed until the wrapped executor has been fully built.

        +

        new DeferredExecutor(delegate)

        Parameters
        delegatewebdriver.promise.Promise<webdriver.CommandExecutor>

        The +promised delegate.

        +

        Instance Methods

        execute(command, callback)code »

        Executes the given 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.

        +

        Specified by: webdriver.CommandExecutor

        Parameters
        commandwebdriver.Command

        The command to execute.

        +
        callbackfunction(Error, {status: number, value: *}=): ?

        the function +to invoke when the command response is ready.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox.html b/docs/module_selenium-webdriver_firefox.html new file mode 100644 index 0000000..cd6a2a2 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox.html @@ -0,0 +1,68 @@ +selenium-webdriver/firefox

        module selenium-webdriver/firefox

        Defines the WebDriver client for Firefox. +Each FirefoxDriver instance will be created with an anonymous profile, +ensuring browser historys do not share session data (cookies, history, cache, +offline storage, etc.)

        +

        Customizing the Firefox Profile

        +

        The Profile class may be used to configure the browser profile used +with WebDriver, with functions to install additional +extensions, configure browser +preferences, and more. For example, you +may wish to include Firebug:

        +
        var firefox = require('selenium-webdriver/firefox');
        +
        +var profile = new firefox.Profile();
        +profile.addExtension('/path/to/firebug.xpi');
        +profile.setPreference('extensions.firebug.showChromeErrors', true);
        +
        +var options = new firefox.Options().setProfile(profile);
        +var driver = new firefox.Driver(options);
        +
        +

        The Profile class may also be used to configure WebDriver based on a +pre-existing browser profile:

        +
        var profile = new firefox.Profile(
        +    '/usr/local/home/bob/.mozilla/firefox/3fgog75h.testing');
        +var options = new firefox.Options().setProfile(profile);
        +var driver = new firefox.Driver(options);
        +
        +

        The FirefoxDriver will never modify a pre-existing profile; instead it will +create a copy for it to modify. By extension, there are certain browser +preferences that are required for WebDriver to function properly and they +will always be overwritten.

        +

        Using a Custom Firefox Binary

        +

        On Windows and OSX, the FirefoxDriver will search for Firefox in its +default installation location:

        +
        • Windows: C:\Program Files and C:\Program Files (x86).
        • Mac OS X: /Applications/Firefox.app
        +

        For Linux, Firefox will be located on the PATH: $(where firefox).

        +

        You can configure WebDriver to start use a custom Firefox installation with +the Binary class:

        +
        var firefox = require('selenium-webdriver/firefox');
        +var binary = new firefox.Binary('/my/firefox/install/dir/firefox-bin');
        +var options = new firefox.Options().setBinary(binary);
        +var driver = new firefox.Driver(options);
        +
        +

        Remote Testing

        +

        You may customize the Firefox binary and profile when running against a +remote Selenium server. Your custom profile will be packaged as a zip and +transfered to the remote host for use. The profile will be transferred +once for each new session. The performance impact should be minimal if +you've only configured a few extra browser preferences. If you have a large +profile with several extensions, you should consider installing it on the +remote host and defining its path via the Options class. Custom +binaries are never copied to remote machines and must be referenced by +installation path.

        +
        var options = new firefox.Options()
        +    .setProfile('/profile/path/on/remote/host')
        +    .setBinary('/install/dir/on/remote/host/firefox-bin');
        +
        +var driver = new (require('selenium-webdriver')).Builder()
        +    .forBrowser('firefox')
        +    .usingServer('http://127.0.0.1:4444/wd/hub')
        +    .setFirefoxOptions(options)
        +    .build();
        +
        +

        Types

        Binary

        Provides a mechanism to configure and launch Firefox in a subprocess for +use with WebDriver.

        +
        Driver

        A WebDriver client for Firefox.

        +
        Options

        Configuration options for the FirefoxDriver.

        +
        Profile

        Models a Firefox proifle directory for use with the FirefoxDriver.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_binary.html b/docs/module_selenium-webdriver_firefox_binary.html new file mode 100644 index 0000000..337332e --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_binary.html @@ -0,0 +1,5 @@ +selenium-webdriver/firefox/binary

        module selenium-webdriver/firefox/binary

        Manages Firefox binaries. This module is considered internal; +users should use selenium-webdriver/firefox.

        +

        Types

        Binary

        Provides a mechanism to configure and launch Firefox in a subprocess for +use with WebDriver.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_binary_class_Binary.html b/docs/module_selenium-webdriver_firefox_binary_class_Binary.html new file mode 100644 index 0000000..0e4bf59 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_binary_class_Binary.html @@ -0,0 +1,20 @@ +Binary

        class Binary

        webdriver.Serializable
        +  └ Binary

        Provides a mechanism to configure and launch Firefox in a subprocess for +use with WebDriver.

        +

        new Binary(opt_exe)

        Parameters
        opt_exestring=

        Path to the Firefox binary to use. If not +specified, will attempt to locate Firefox on the current system.

        +

        Instance Methods

        addArguments(var_args)code »

        Add arguments to the command line used to start Firefox.

        +
        Parameters
        var_args...(string|Array<string>)

        Either the arguments to add as +varargs, or the arguments as an array.

        +

        launch(profile)code »

        Launches Firefox and returns a promise that will be fulfilled when the +process terminates.

        +
        Parameters
        profilestring

        Path to the profile directory to use.

        +
        Returns
        webdriver.promise.Promise

        A promise for the handle to the +started subprocess.

        +

        serialize()code »

        Returns a promise for the wire representation of this binary. Note: the +FirefoxDriver only supports passing the path to the binary executable over +the wire; all command line arguments and environment variables will be +discarded.

        +

        Overrides: webdriver.Serializable

        Returns
        webdriver.promise.Promise

        A promise for this binary's wire +representation.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_class_Binary.html b/docs/module_selenium-webdriver_firefox_class_Binary.html new file mode 100644 index 0000000..0d32712 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_class_Binary.html @@ -0,0 +1,20 @@ +Binary

        class Binary

        webdriver.Serializable
        +  └ Binary
        Alias for module(selenium-webdriver/firefox/binary).Binary

        Provides a mechanism to configure and launch Firefox in a subprocess for +use with WebDriver.

        +

        new Binary(opt_exe)

        Parameters
        opt_exestring=

        Path to the Firefox binary to use. If not +specified, will attempt to locate Firefox on the current system.

        +

        Instance Methods

        addArguments(var_args)code »

        Add arguments to the command line used to start Firefox.

        +
        Parameters
        var_args...(string|Array<string>)

        Either the arguments to add as +varargs, or the arguments as an array.

        +

        launch(profile)code »

        Launches Firefox and returns a promise that will be fulfilled when the +process terminates.

        +
        Parameters
        profilestring

        Path to the profile directory to use.

        +
        Returns
        webdriver.promise.Promise

        A promise for the handle to the +started subprocess.

        +

        serialize()code »

        Returns a promise for the wire representation of this binary. Note: the +FirefoxDriver only supports passing the path to the binary executable over +the wire; all command line arguments and environment variables will be +discarded.

        +

        Overrides: webdriver.Serializable

        Returns
        webdriver.promise.Promise

        A promise for this binary's wire +representation.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_class_Driver.html b/docs/module_selenium-webdriver_firefox_class_Driver.html new file mode 100644 index 0000000..0c734e6 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_class_Driver.html @@ -0,0 +1,276 @@ +Driver

        class Driver

        webdriver.WebDriver
        +  └ Driver

        A WebDriver client for Firefox.

        +

        new Driver(opt_config, opt_flow)

        Parameters
        opt_config?(Capabilities|Object)=

        The +configuration options for this driver, specified as either an +Options or webdriver.Capabilities, or as a raw hash +object.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The flow to +schedule commands through. Defaults to the active flow object.

        +

        Instance Methods

        actions()code »

        Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

        +
        driver.actions().
        +    mouseDown(element1).
        +    mouseMove(element2).
        +    mouseUp().
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.ActionSequence

        A new action sequence for this instance.

        +

        <T> call(fn, opt_scope, var_args)code »

        Schedules a command to execute a custom function.

        +

        Defined by: webdriver.WebDriver

        Parameters
        fnfunction(...?): (T|webdriver.promise.Promise<T>)

        The function to +execute.

        +
        opt_scope?Object=

        The object in whose scope to execute the function.

        +
        var_args...*

        Any arguments to pass to the function.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved' +with the function's result.

        +

        close()code »

        Schedules a command to close the current window.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when this command has completed.

        +

        controlFlow()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.ControlFlow

        The control flow used by this +instance.

        +

        <T> executeAsyncScript(script, var_args)code »

        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 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 #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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Example #1: Performing a sleep that is synchronized with the currently +selected window:

        +
        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 #2: Synchronizing a test with an AJAX application:

        +
        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');
        +
        +

        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.

        +
        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.responseText);
        +    }
        +  }
        +  xhr.send('');
        +}).then(function(str) {
        +  console.log(JSON.parse(str)['food']);
        +});
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        <T> executeScript(script, var_args)code »

        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 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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        findElement(locator)code »

        Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator to use.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedule a command to search for multiple elements on the page.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        get(url)code »

        Schedules a command to navigate to the given URL.

        +

        Defined by: webdriver.WebDriver

        Parameters
        urlstring

        The fully qualified URL to open.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the document has finished loading.

        +

        getAllWindowHandles()code »

        Schedules a command to retrieve the current list of available window handles.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<Array<string>>

        A promise that will +be resolved with an array of window handles.

        +

        getCapabilities()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Capabilities>

        A promise +that will resolve with the this instance's capabilities.

        +

        getCurrentUrl()code »

        Schedules a command to retrieve the URL of the current page.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current URL.

        +

        getPageSource()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page source.

        +

        getSession()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Session>

        A promise for this +client's session.

        +

        getTitle()code »

        Schedules a command to retrieve the current page's title.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page's title.

        +

        getWindowHandle()code »

        Schedules a command to retrieve they current window handle.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current window handle.

        +

        isElementPresent(locatorOrElement)code »

        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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator to use, or the actual +DOM element to be located by the server.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the element is present on the page.

        +

        manage()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.Options

        The options interface for this +instance.

        +


        quit()code »

        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.

        +

        Overrides: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the command has completed.

        +

        <T> schedule(command, description)code »

        Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

        +

        Defined by: webdriver.WebDriver

        Parameters
        commandwebdriver.Command

        The command to schedule.

        +
        descriptionstring

        A description of the command for debugging.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved +with the command result.

        +

        setFileDetector(detector)code »

        This function is a no-op as file detectors are not supported by this +implementation.

        +

        Overrides: webdriver.WebDriver

        Parameters
        detectorwebdriver.FileDetector

        The detector to use or null.

        +

        sleep(ms)code »

        Schedules a command to make the driver sleep for the given amount of time.

        +

        Defined by: webdriver.WebDriver

        Parameters
        msnumber

        The amount of time, in milliseconds, to sleep.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the sleep has finished.

        +

        switchTo()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.TargetLocator

        The target locator interface for +this instance.

        +

        takeScreenshot()code »

        Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

        +
        1. Entire page +
        2. Current window +
        3. Visible portion of the current frame +
        4. The screenshot of the entire display containing the browser +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

        +

        touchActions()code »

        Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

        +
        driver.touchActions().
        +    tap(element1).
        +    doubleTap(element2).
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.TouchSequence

        A new touch sequence for this instance.

        +

        <T> wait(condition, opt_timeout, opt_message)code »

        Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

        +

        For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

        +

        Example: waiting up to 10 seconds for an element to be present and visible +on the page.

        +
        var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        +button.click();
        +
        +

        This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

        +

        Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

        +
        var started = startTestServer();
        +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        +driver.get(getServerUrl());
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

        The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

        +
        opt_timeoutnumber=

        How long to wait for the condition to be true.

        +
        opt_messagestring=

        An optional message to use if the wait times +out.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_class_Options.html b/docs/module_selenium-webdriver_firefox_class_Options.html new file mode 100644 index 0000000..dd102dc --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_class_Options.html @@ -0,0 +1,19 @@ +Options

        class Options

        Configuration options for the FirefoxDriver.

        +

        new Options()

        Parameters
        None.

        Instance Methods

        setBinary(binary)code »

        Sets the binary to use. The binary may be specified as the path to a Firefox +executable, or as a Binary object.

        +
        Parameters
        binary(string|Binary)

        The binary to use.

        +
        Returns
        Options

        A self reference.

        +

        setLoggingPreferences(prefs)code »

        Sets the logging preferences for the new session.

        +
        Parameters
        prefswebdriver.logging.Preferences

        The logging preferences.

        +
        Returns
        Options

        A self reference.

        +

        setProfile(profile)code »

        Sets the profile to use. The profile may be specified as a +Profile object or as the path to an existing Firefox profile to use +as a template.

        +
        Parameters
        profile(string|Profile)

        The profile to use.

        +
        Returns
        Options

        A self reference.

        +

        setProxy(proxy)code »

        Sets the proxy to use.

        +
        Parameters
        proxyselenium-webdriver.ProxyConfig

        The proxy configuration to use.

        +
        Returns
        Options

        A self reference.

        +

        toCapabilities(opt_remote)code »

        Converts these options to a webdriver.Capabilities instance.

        +
        Parameters
        opt_remote?
        Returns
        webdriver.Capabilities

        A new capabilities object.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_class_Profile.html b/docs/module_selenium-webdriver_firefox_class_Profile.html new file mode 100644 index 0000000..d672120 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_class_Profile.html @@ -0,0 +1,50 @@ +Profile

        class Profile

        webdriver.Serializable
        +  └ Profile
        Alias for module(selenium-webdriver/firefox/profile).Profile

        Models a Firefox proifle directory for use with the FirefoxDriver. The +Proifle directory uses an in-memory model until #writeToDisk +is called.

        +

        new Profile(opt_dir)

        Parameters
        opt_dirstring=

        Path to an existing Firefox profile directory to +use a template for this profile. If not specified, a blank profile will +be used.

        +

        Instance Methods

        acceptUntrustedCerts()code »

        Returns
        boolean

        Whether the FirefoxDriver is configured to automatically +accept untrusted SSL certificates.

        +

        addExtension(extension)code »

        Registers an extension to be included with this profile.

        +
        Parameters
        extensionstring

        Path to the extension to include, as either an +unpacked extension directory or the path to a xpi file.

        +

        assumeUntrustedCertIssuer()code »

        Returns
        boolean

        Whether to assume untrusted certs come from untrusted +issuers.

        +

        encode()code »

        Encodes this profile as a zipped, base64 encoded directory.

        +
        Returns
        webdriver.promise.Promise

        A promise for the encoded profile.

        +

        getPort()code »

        Returns
        number

        The port this profile is currently configured to use, or +0 if the port will be selected at random when the profile is written +to disk.

        +

        getPreference(key)code »

        Returns the currently configured value of a profile preference. This does +not include any defaults defined in the profile's template directory user.js +file (if a template were specified on construction).

        +
        Parameters
        keystring

        The desired preference.

        +
        Returns
        (string|number|boolean|undefined)

        The current value of the +requested preference.

        +

        nativeEventsEnabled()code »

        Returns whether native events are enabled in this profile.

        +
        Returns
        boolean

        .

        +

        serialize()code »

        Encodes this profile as a zipped, base64 encoded directory.

        +

        Overrides: webdriver.Serializable

        Returns
        webdriver.promise.Promise

        A promise for the encoded profile.

        +

        setAcceptUntrustedCerts(value)code »

        Sets whether the FirefoxDriver should automatically accept untrusted SSL +certificates.

        +
        Parameters
        valueboolean

        .

        +

        setAssumeUntrustedCertIssuer(value)code »

        Sets whether to assume untrusted certificates come from untrusted issuers.

        +
        Parameters
        valueboolean

        .

        +

        setNativeEventsEnabled(enabled)code »

        Sets whether to use native events with this profile.

        +
        Parameters
        enabledboolean

        .

        +

        setPort(port)code »

        Sets the port to use for the WebDriver extension loaded by this profile.

        +
        Parameters
        portnumber

        The desired port, or 0 to use any free port.

        +

        setPreference(key, value)code »

        Sets a desired preference for this profile.

        +
        Parameters
        keystring

        The preference key.

        +
        value(string|number|boolean)

        The preference value.

        +
        Throws
        Error

        If attempting to set a frozen preference.

        +

        writeToDisk(opt_excludeWebDriverExt)code »

        Writes this profile to disk.

        +
        Parameters
        opt_excludeWebDriverExtboolean=

        Whether to exclude the WebDriver +extension from the generated profile. Used to reduce the size of an +encoded profile since the server will always install +the extension itself.

        +
        Returns
        webdriver.promise.Promise

        A promise for the path to the new +profile directory.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_extension.html b/docs/module_selenium-webdriver_firefox_extension.html new file mode 100644 index 0000000..1dc08e4 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_extension.html @@ -0,0 +1,8 @@ +selenium-webdriver/firefox/extension

        module selenium-webdriver/firefox/extension

        Utilities for working with Firefox extensions.

        +

        Functions

        install(extension, dir)code »

        Installs an extension to the given directory.

        +
        Parameters
        extensionstring

        Path to the extension to install, as either a xpi +file or a directory.

        +
        dirstring

        Path to the directory to install the extension in.

        +
        Returns
        webdriver.promise.Promise

        A promise for the add-on ID once +installed.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_profile.html b/docs/module_selenium-webdriver_firefox_profile.html new file mode 100644 index 0000000..2206a3a --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_profile.html @@ -0,0 +1,13 @@ +selenium-webdriver/firefox/profile

        module selenium-webdriver/firefox/profile

        Profile management module. This module is considered internal; +users should use selenium-webdriver/firefox.

        +

        Functions

        decode(data)code »

        Decodes a base64 encoded profile.

        +
        Parameters
        datastring

        The base64 encoded string.

        +
        Returns
        webdriver.promise.Promise

        A promise for the path to the decoded +profile directory.

        +

        loadUserPrefs(f)code »

        Parses a user.js file in a Firefox profile directory.

        +
        Parameters
        fstring

        Path to the file to parse.

        +
        Returns
        webdriver.promise.Promise

        A promise for the parsed preferences as +a JSON object. If the file does not exist, an empty object will be +returned.

        +

        Types

        Profile

        Models a Firefox proifle directory for use with the FirefoxDriver.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_firefox_profile_class_Profile.html b/docs/module_selenium-webdriver_firefox_profile_class_Profile.html new file mode 100644 index 0000000..055d055 --- /dev/null +++ b/docs/module_selenium-webdriver_firefox_profile_class_Profile.html @@ -0,0 +1,50 @@ +Profile

        class Profile

        webdriver.Serializable
        +  └ Profile

        Models a Firefox proifle directory for use with the FirefoxDriver. The +Proifle directory uses an in-memory model until #writeToDisk +is called.

        +

        new Profile(opt_dir)

        Parameters
        opt_dirstring=

        Path to an existing Firefox profile directory to +use a template for this profile. If not specified, a blank profile will +be used.

        +

        Instance Methods

        acceptUntrustedCerts()code »

        Returns
        boolean

        Whether the FirefoxDriver is configured to automatically +accept untrusted SSL certificates.

        +

        addExtension(extension)code »

        Registers an extension to be included with this profile.

        +
        Parameters
        extensionstring

        Path to the extension to include, as either an +unpacked extension directory or the path to a xpi file.

        +

        assumeUntrustedCertIssuer()code »

        Returns
        boolean

        Whether to assume untrusted certs come from untrusted +issuers.

        +

        encode()code »

        Encodes this profile as a zipped, base64 encoded directory.

        +
        Returns
        webdriver.promise.Promise

        A promise for the encoded profile.

        +

        getPort()code »

        Returns
        number

        The port this profile is currently configured to use, or +0 if the port will be selected at random when the profile is written +to disk.

        +

        getPreference(key)code »

        Returns the currently configured value of a profile preference. This does +not include any defaults defined in the profile's template directory user.js +file (if a template were specified on construction).

        +
        Parameters
        keystring

        The desired preference.

        +
        Returns
        (string|number|boolean|undefined)

        The current value of the +requested preference.

        +

        nativeEventsEnabled()code »

        Returns whether native events are enabled in this profile.

        +
        Returns
        boolean

        .

        +

        serialize()code »

        Encodes this profile as a zipped, base64 encoded directory.

        +

        Overrides: webdriver.Serializable

        Returns
        webdriver.promise.Promise

        A promise for the encoded profile.

        +

        setAcceptUntrustedCerts(value)code »

        Sets whether the FirefoxDriver should automatically accept untrusted SSL +certificates.

        +
        Parameters
        valueboolean

        .

        +

        setAssumeUntrustedCertIssuer(value)code »

        Sets whether to assume untrusted certificates come from untrusted issuers.

        +
        Parameters
        valueboolean

        .

        +

        setNativeEventsEnabled(enabled)code »

        Sets whether to use native events with this profile.

        +
        Parameters
        enabledboolean

        .

        +

        setPort(port)code »

        Sets the port to use for the WebDriver extension loaded by this profile.

        +
        Parameters
        portnumber

        The desired port, or 0 to use any free port.

        +

        setPreference(key, value)code »

        Sets a desired preference for this profile.

        +
        Parameters
        keystring

        The preference key.

        +
        value(string|number|boolean)

        The preference value.

        +
        Throws
        Error

        If attempting to set a frozen preference.

        +

        writeToDisk(opt_excludeWebDriverExt)code »

        Writes this profile to disk.

        +
        Parameters
        opt_excludeWebDriverExtboolean=

        Whether to exclude the WebDriver +extension from the generated profile. Used to reduce the size of an +encoded profile since the server will always install +the extension itself.

        +
        Returns
        webdriver.promise.Promise

        A promise for the path to the new +profile directory.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_http.html b/docs/module_selenium-webdriver_http.html index a5b2f98..bd3de77 100644 --- a/docs/module_selenium-webdriver_http.html +++ b/docs/module_selenium-webdriver_http.html @@ -1,4 +1,9 @@ -selenium-webdriver/http

        Module selenium-webdriver/http

        code »

        Defines a the webdriver.http.Client for use with - NodeJS.

        Classes

        Executor
        A command executor that communicates with a server using the WebDriver - command protocol.
        HttpClient
        A webdriver.http.Client implementation using Node's built-in http - module.
        Request
        Describes a partial HTTP request.
        Response
        Represents a HTTP response.
        Show:
        \ No newline at end of file +selenium-webdriver/http

        module selenium-webdriver/http

        Defines the webdriver.http.Client for use with +NodeJS.

        +

        Types

        Executor

        A command executor that communicates with a server using the WebDriver +command protocol.

        +
        HttpClient

        A webdriver.http.Client implementation using Node's built-in http +module.

        +
        Request

        Describes a partial HTTP request.

        +
        Response

        Represents a HTTP response.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_http_class_Executor.html b/docs/module_selenium-webdriver_http_class_Executor.html new file mode 100644 index 0000000..a884e88 --- /dev/null +++ b/docs/module_selenium-webdriver_http_class_Executor.html @@ -0,0 +1,22 @@ +Executor

        class Executor

        Alias for webdriver.http.Executor
        All implemented interfaces
        webdriver.CommandExecutor

        A command executor that communicates with a server using the WebDriver +command protocol.

        +

        new Executor(client)

        Parameters
        clientwebdriver.http.Client

        The client to use when sending +requests to the server.

        +

        Instance Methods

        defineCommand(name, method, path)code »

        Defines a new command for use with this executor. When a command is sent, +the path will be preprocessed using the command's parameters; any +path segments prefixed with ":" will be replaced by the parameter of the +same name. For example, given "/person/:name" and the parameters +"{name: 'Bob'}", the final command path will be "/person/Bob".

        +
        Parameters
        namestring

        The command name.

        +
        methodstring

        The HTTP method to use when sending this command.

        +
        pathstring

        The path to send the command to, relative to +the WebDriver server's command root and of the form +"/path/:variable/segment".

        +

        execute(command, callback)code »

        Executes the given 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.

        +

        Specified by: webdriver.CommandExecutor

        Parameters
        commandwebdriver.Command

        The command to execute.

        +
        callbackfunction(Error, {status: number, value: *}=): ?

        the function +to invoke when the command response is ready.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_http_class_HttpClient.html b/docs/module_selenium-webdriver_http_class_HttpClient.html index 370359b..102cb7b 100644 --- a/docs/module_selenium-webdriver_http_class_HttpClient.html +++ b/docs/module_selenium-webdriver_http_class_HttpClient.html @@ -1,2 +1,16 @@ -HttpClient

        Class HttpClient

        code »
        All implemented interfaces:
        webdriver.http.Client

        A webdriver.http.Client implementation using Node's built-in http - module.

        Constructor

        HttpClient ( serverUrl )
        Parameters
        serverUrl: string
        URL for the WebDriver server to send commands to.
        Show:

        Instance Methods

        code »send ( httpRequest, callback )
        Parameters
        httpRequest
        callback

        Instance Properties

        Base options for each request.

        \ No newline at end of file +HttpClient

        class HttpClient

        All implemented interfaces
        webdriver.http.Client

        A webdriver.http.Client implementation using Node's built-in http +module.

        +

        new HttpClient(serverUrl, opt_agent, opt_proxy)

        Parameters
        serverUrlstring

        URL for the WebDriver server to send commands to.

        +
        opt_agent?http.Agent=

        The agent to use for each request. +Defaults to http.globalAgent.

        +
        opt_proxystring=

        The proxy to use for the connection to the server. +Default is to use no proxy.

        +

        Instance Methods

        send(request, callback)code »

        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 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.

        +

        Specified by: webdriver.http.Client

        Parameters
        requestwebdriver.http.Request

        The request to send.

        +
        callbackfunction(Error, webdriver.http.Response=): ?

        the function to +invoke when the server's response is ready.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_http_class_Request.html b/docs/module_selenium-webdriver_http_class_Request.html new file mode 100644 index 0000000..755cc7f --- /dev/null +++ b/docs/module_selenium-webdriver_http_class_Request.html @@ -0,0 +1,12 @@ +Request

        class Request

        Alias for webdriver.http.Request

        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 +webdriver.http.Client's responsibility to build the full URL for the +final request.

        +

        new Request(method, path, opt_data)

        Parameters
        methodstring

        The HTTP method to use for the request.

        +
        pathstring

        Path on the server to send the request to.

        +
        opt_data?Object=

        This request's JSON data.

        +

        Instance Methods

        toString()code »

        Returns
        string

        Instance Properties

        dataObject

        This request's body.

        +
        headersObject<?, (string|number)>

        The headers to send with the request.

        +
        methodstring

        The HTTP method to use for the request.

        +
        pathstring

        The path on the server to send the request to.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_http_class_Response.html b/docs/module_selenium-webdriver_http_class_Response.html new file mode 100644 index 0000000..cbc19cc --- /dev/null +++ b/docs/module_selenium-webdriver_http_class_Response.html @@ -0,0 +1,13 @@ +Response

        class Response

        Alias for webdriver.http.Response

        Represents a HTTP response.

        +

        new Response(status, headers, body)

        Parameters
        statusnumber

        The response code.

        +
        headersObject<?, string>

        The response headers. All header +names will be converted to lowercase strings for consistent lookups.

        +
        bodystring

        The response body.

        +

        Instance Methods

        toString()code »

        Returns
        string

        Instance Properties

        bodystring

        The response body.

        +
        headersObject<?, string>

        The response body.

        +
        statusnumber

        The HTTP response code.

        +

        Static Functions

        Response.fromXmlHttpRequest(xhr)code »

        Builds a webdriver.http.Response from a XMLHttpRequest or +XDomainRequest response object.

        +
        Parameters
        xhr(XDomainRequest|XMLHttpRequest)

        The request to parse.

        +
        Returns
        webdriver.http.Response

        The parsed response.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_http_util.html b/docs/module_selenium-webdriver_http_util.html index e85d100..76dc809 100644 --- a/docs/module_selenium-webdriver_http_util.html +++ b/docs/module_selenium-webdriver_http_util.html @@ -1,5 +1,17 @@ -selenium-webdriver/http/util

        Module selenium-webdriver/http/util

        code »

        Various HTTP utilities.

        Show:

        Functions

        Queries a WebDriver server for its current status.

        Parameters
        url: string
        Base URL of the server to query.
        Returns
        A promise that resolves with - a hash of the server status.

        Waits for a WebDriver server to be healthy and accepting requests.

        Parameters
        url: string
        Base URL of the server to query.
        timeout: number
        How long to wait for the server.
        Returns
        A promise that will resolve when the - server is ready.

        Polls a URL with GET requests until it returns a 2xx response or the - timeout expires.

        Parameters
        url: string
        The URL to poll.
        timeout: number
        How long to wait, in milliseconds.
        Returns
        A promise that will resolve when the - URL responds with 2xx.
        \ No newline at end of file +selenium-webdriver/http/util

        module selenium-webdriver/http/util

        Various HTTP utilities.

        +

        Functions

        getStatus(url)code »

        Queries a WebDriver server for its current status.

        +
        Parameters
        urlstring

        Base URL of the server to query.

        +
        Returns
        webdriver.promise.Promise<Object>

        A promise that resolves with +a hash of the server status.

        +

        waitForServer(url, timeout)code »

        Waits for a WebDriver server to be healthy and accepting requests.

        +
        Parameters
        urlstring

        Base URL of the server to query.

        +
        timeoutnumber

        How long to wait for the server.

        +
        Returns
        webdriver.promise.Promise

        A promise that will resolve when the +server is ready.

        +

        waitForUrl(url, timeout)code »

        Polls a URL with GET requests until it returns a 2xx response or the +timeout expires.

        +
        Parameters
        urlstring

        The URL to poll.

        +
        timeoutnumber

        How long to wait, in milliseconds.

        +
        Returns
        webdriver.promise.Promise

        A promise that will resolve when the +URL responds with 2xx.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_ie.html b/docs/module_selenium-webdriver_ie.html new file mode 100644 index 0000000..315f73d --- /dev/null +++ b/docs/module_selenium-webdriver_ie.html @@ -0,0 +1,11 @@ +selenium-webdriver/ie

        module selenium-webdriver/ie

        Defines a WebDriver client for Microsoft's +Internet Explorer. Before using the IEDriver, you must download the latest +IEDriverServer +and place it on your +PATH. You must also apply +the system configuration outlined on the Selenium project +wiki

        +

        Types

        Driver

        A WebDriver client for Microsoft's Internet Explorer.

        +
        Level

        No description.

        +
        Options

        Class for managing IEDriver specific options.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_ie_class_Driver.html b/docs/module_selenium-webdriver_ie_class_Driver.html new file mode 100644 index 0000000..08c9bae --- /dev/null +++ b/docs/module_selenium-webdriver_ie_class_Driver.html @@ -0,0 +1,274 @@ +Driver

        class Driver

        webdriver.WebDriver
        +  └ Driver

        A WebDriver client for Microsoft's Internet Explorer.

        +

        new Driver(opt_config, opt_flow)

        Parameters
        opt_config?(Capabilities|Options)=

        The configuration +options.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The control flow to use, or +null to use the currently active flow.

        +

        Instance Methods

        actions()code »

        Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

        +
        driver.actions().
        +    mouseDown(element1).
        +    mouseMove(element2).
        +    mouseUp().
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.ActionSequence

        A new action sequence for this instance.

        +

        <T> call(fn, opt_scope, var_args)code »

        Schedules a command to execute a custom function.

        +

        Defined by: webdriver.WebDriver

        Parameters
        fnfunction(...?): (T|webdriver.promise.Promise<T>)

        The function to +execute.

        +
        opt_scope?Object=

        The object in whose scope to execute the function.

        +
        var_args...*

        Any arguments to pass to the function.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved' +with the function's result.

        +

        close()code »

        Schedules a command to close the current window.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when this command has completed.

        +

        controlFlow()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.ControlFlow

        The control flow used by this +instance.

        +

        <T> executeAsyncScript(script, var_args)code »

        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 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 #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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Example #1: Performing a sleep that is synchronized with the currently +selected window:

        +
        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 #2: Synchronizing a test with an AJAX application:

        +
        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');
        +
        +

        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.

        +
        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.responseText);
        +    }
        +  }
        +  xhr.send('');
        +}).then(function(str) {
        +  console.log(JSON.parse(str)['food']);
        +});
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        <T> executeScript(script, var_args)code »

        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 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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        findElement(locator)code »

        Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator to use.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedule a command to search for multiple elements on the page.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        get(url)code »

        Schedules a command to navigate to the given URL.

        +

        Defined by: webdriver.WebDriver

        Parameters
        urlstring

        The fully qualified URL to open.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the document has finished loading.

        +

        getAllWindowHandles()code »

        Schedules a command to retrieve the current list of available window handles.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<Array<string>>

        A promise that will +be resolved with an array of window handles.

        +

        getCapabilities()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Capabilities>

        A promise +that will resolve with the this instance's capabilities.

        +

        getCurrentUrl()code »

        Schedules a command to retrieve the URL of the current page.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current URL.

        +

        getPageSource()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page source.

        +

        getSession()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Session>

        A promise for this +client's session.

        +

        getTitle()code »

        Schedules a command to retrieve the current page's title.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page's title.

        +

        getWindowHandle()code »

        Schedules a command to retrieve they current window handle.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current window handle.

        +

        isElementPresent(locatorOrElement)code »

        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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator to use, or the actual +DOM element to be located by the server.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the element is present on the page.

        +

        manage()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.Options

        The options interface for this +instance.

        +


        quit()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the command has completed.

        +

        <T> schedule(command, description)code »

        Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

        +

        Defined by: webdriver.WebDriver

        Parameters
        commandwebdriver.Command

        The command to schedule.

        +
        descriptionstring

        A description of the command for debugging.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved +with the command result.

        +

        setFileDetector(detector)code »

        This function is a no-op as file detectors are not supported by this +implementation.

        +

        Overrides: webdriver.WebDriver

        Parameters
        detectorwebdriver.FileDetector

        The detector to use or null.

        +

        sleep(ms)code »

        Schedules a command to make the driver sleep for the given amount of time.

        +

        Defined by: webdriver.WebDriver

        Parameters
        msnumber

        The amount of time, in milliseconds, to sleep.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the sleep has finished.

        +

        switchTo()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.TargetLocator

        The target locator interface for +this instance.

        +

        takeScreenshot()code »

        Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

        +
        1. Entire page +
        2. Current window +
        3. Visible portion of the current frame +
        4. The screenshot of the entire display containing the browser +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

        +

        touchActions()code »

        Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

        +
        driver.touchActions().
        +    tap(element1).
        +    doubleTap(element2).
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.TouchSequence

        A new touch sequence for this instance.

        +

        <T> wait(condition, opt_timeout, opt_message)code »

        Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

        +

        For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

        +

        Example: waiting up to 10 seconds for an element to be present and visible +on the page.

        +
        var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        +button.click();
        +
        +

        This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

        +

        Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

        +
        var started = startTestServer();
        +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        +driver.get(getServerUrl());
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

        The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

        +
        opt_timeoutnumber=

        How long to wait for the condition to be true.

        +
        opt_messagestring=

        An optional message to use if the wait times +out.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_ie_class_Options.html b/docs/module_selenium-webdriver_ie_class_Options.html new file mode 100644 index 0000000..b250383 --- /dev/null +++ b/docs/module_selenium-webdriver_ie_class_Options.html @@ -0,0 +1,89 @@ +Options

        class Options

        Class for managing IEDriver specific options.

        +

        new Options()

        Parameters
        None.

        Instance Methods

        addArguments(var_args)code »

        Specifies command-line switches to use when launching Internet Explorer. +This is only valid when used with #forceCreateProcessApi.

        +
        Parameters
        var_args...(string|Array<string>)

        The arguments to add.

        +
        Returns
        Options

        A self reference.

        +

        browserAttachTimeout(timeout)code »

        Configures the timeout, in milliseconds, that the driver will attempt to +located and attach to a newly opened instance of Internet Explorer. The +default is zero, which indicates waiting indefinitely.

        +
        Parameters
        timeoutnumber

        How long to wait for IE.

        +
        Returns
        Options

        A self reference.

        +

        enableElementCacheCleanup(enable)code »

        Configures whether the driver should attempt to remove obsolete +WebElements from its internal cache on +page navigation (true by default). Disabling this option will cause the +driver to run with a larger memory footprint.

        +
        Parameters
        enableboolean

        Whether to enable element reference cleanup.

        +
        Returns
        Options

        A self reference.

        +

        enablePersistentHover(enable)code »

        Configures whether to enable persistent mouse hovering (true by default). +Persistent hovering is achieved by continuously firing mouse over events at +the last location the mouse cursor has been moved to.

        +
        Parameters
        enableboolean

        Whether to enable persistent hovering.

        +
        Returns
        Options

        A self reference.

        +

        ensureCleanSession(cleanSession)code »

        Configures whether to clear the cache, cookies, history, and saved form data +before starting the browser. Using this capability will clear session data +for all running instances of Internet Explorer, including those started +manually.

        +
        Parameters
        cleanSessionboolean

        Whether to clear all session data on startup.

        +
        Returns
        Options

        A self reference.

        +

        forceCreateProcessApi(force)code »

        Configures whether to launch Internet Explorer using the CreateProcess API. +If this option is not specified, IE is launched using IELaunchURL, if +available. For IE 8 and above, this option requires the TabProcGrowth +registry value to be set to 0.

        +
        Parameters
        forceboolean

        Whether to use the CreateProcess API.

        +
        Returns
        Options

        A self reference.

        +

        ignoreZoomSetting(ignore)code »

        Indicates whether to skip the check that the browser's zoom level is set to +100%.

        +
        Parameters
        ignore?
        Returns
        Options

        ore Whether to ignore the browser's zoom level settings.

        +

        initialBrowserUrl(url)code »

        Sets the initial URL loaded when IE starts. This is intended to be used with +#ignoreProtectedModeSettings to allow the user to initialize IE in +the proper Protected Mode zone. Setting this option may cause browser +instability or flaky and unresponsive code. Only "best effort" support is +provided when using this option.

        +
        Parameters
        urlstring

        The initial browser URL.

        +
        Returns
        Options

        A self reference.

        +

        introduceFlakinessByIgnoringProtectedModeSettings(ignoreSettings)code »

        Whether to disable the protected mode settings check when the session is +created. Disbling this setting may lead to significant instability as the +browser may become unresponsive/hang. Only "best effort" support is provided +when using this capability.

        +

        For more information, refer to the IEDriver's +required system configuration.

        +
        Parameters
        ignoreSettingsboolean

        Whether to ignore protected mode settings.

        +
        Returns
        Options

        A self reference.

        +

        requireWindowFocus(require)code »

        Configures whether to require the IE window to have input focus before +performing any user interactions (i.e. mouse or keyboard events). This +option is disabled by default, but delivers much more accurate interaction +events when enabled.

        +
        Parameters
        requireboolean

        Whether to require window focus.

        +
        Returns
        Options

        A self reference.

        +

        setExtractPath(path)code »

        Sets the path of the temporary data directory to use.

        +
        Parameters
        pathstring

        The log file path.

        +
        Returns
        Options

        A self reference.

        +

        setHost(host)code »

        Sets the IP address of the driver's host adapter.

        +
        Parameters
        hoststring

        The IP address to use.

        +
        Returns
        Options

        A self reference.

        +

        setLogFile(path)code »

        Sets the path to the log file the driver should log to.

        +
        Parameters
        pathstring

        The log file path.

        +
        Returns
        Options

        A self reference.

        +

        setLogLevel(level)code »

        Sets the IEDriverServer's logging level.

        +
        Parameters
        levelstring

        The logging level.

        +
        Returns
        Options

        A self reference.

        +

        setProxy(proxy)code »

        Sets the proxy settings for the new session.

        +
        Parameters
        proxyselenium-webdriver.ProxyConfig

        The proxy configuration to use.

        +
        Returns
        Options

        A self reference.

        +

        silent(silent)code »

        Sets whether the driver should start in silent mode.

        +
        Parameters
        silentboolean

        Whether to run in silent mode.

        +
        Returns
        Options

        A self reference.

        +

        toCapabilities(opt_capabilities)code »

        Converts this options instance to a webdriver.Capabilities object.

        +
        Parameters
        opt_capabilities?Capabilities=

        The capabilities to merge +these options into, if any.

        +
        Returns
        webdriver.Capabilities

        The capabilities.

        +

        usePerProcessProxy(enable)code »

        Configures whether proxies should be configured on a per-process basis. If +not set, setting a proxy will configure the system +proxy. The default behavior is to use the system proxy.

        +
        Parameters
        enableboolean

        Whether to enable per-process proxy settings.

        +
        Returns
        Options

        A self reference.

        +

        Static Functions

        Options.fromCapabilities(capabilities)code »

        Extracts the IEDriver specific options from the given capabilities +object.

        +
        Parameters
        capabilitiesCapabilities

        The capabilities object.

        +
        Returns
        Options

        The IEDriver options.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_ie_enum_Level.html b/docs/module_selenium-webdriver_ie_enum_Level.html new file mode 100644 index 0000000..4c5bd53 --- /dev/null +++ b/docs/module_selenium-webdriver_ie_enum_Level.html @@ -0,0 +1 @@ +Level
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_io.html b/docs/module_selenium-webdriver_io.html index aabdf7f..78f6b5e 100644 --- a/docs/module_selenium-webdriver_io.html +++ b/docs/module_selenium-webdriver_io.html @@ -1,4 +1,37 @@ -selenium-webdriver/io

        Module selenium-webdriver/io

        code »
        Show:

        Functions

        code »findInPath ( file, opt_checkCwd )?string

        Searches the PATH environment variable for the given file.

        Parameters
        file: string
        The file to locate on the PATH.
        opt_checkCwd: boolean=
        Whether to always start with the search with - the current working directory, regardless of whether it is explicitly - listed on the PATH.
        Returns
        Path to the located file, or null if it could - not be found.
        \ No newline at end of file +selenium-webdriver/io

        module selenium-webdriver/io

        Functions

        copy(src, dst)code »

        Copies one file to another.

        +
        Parameters
        srcstring

        The source file.

        +
        dststring

        The destination file.

        +
        Returns
        webdriver.promise.Promise

        A promise for the copied file's path.

        +

        copyDir(src, dst, opt_exclude)code »

        Recursively copies the contents of one directory to another.

        +
        Parameters
        srcstring

        The source directory to copy.

        +
        dststring

        The directory to copy into.

        +
        opt_exclude?(RegEx|function(string): boolean)=

        An exclusion filter +as either a regex or predicate function. All files matching this filter +will not be copied.

        +
        Returns
        webdriver.promise.Promise

        A promise for the destination +directory's path once all files have been copied.

        +

        exists(path)code »

        Tests if a file path exists.

        +
        Parameters
        pathstring

        The path to test.

        +
        Returns
        webdriver.promise.Promise

        A promise for whether the file exists.

        +

        findInPath(file, opt_checkCwd)code »

        Searches the PATH environment variable for the given file.

        +
        Parameters
        filestring

        The file to locate on the PATH.

        +
        opt_checkCwdboolean=

        Whether to always start with the search with +the current working directory, regardless of whether it is explicitly +listed on the PATH.

        +
        Returns
        ?string

        Path to the located file, or null if it could +not be found.

        +

        rmDir(path)code »

        Recursively removes a directory and all of its contents. This is equivalent +to rm -rf on a POSIX system.

        +
        Parameters
        pathstring

        Path to the directory to remove.

        +
        Returns
        webdriver.promise.Promise

        A promise to be resolved when the operation has +completed.

        +

        tmpDir()code »

        Returns
        webdriver.promise.Promise

        A promise for the path to a temporary +directory.

        +

        tmpFile(opt_options)code »

        Parameters
        opt_options{postfix: string}=

        Temporary file options.

        +
        Returns
        webdriver.promise.Promise

        A promise for the path to a temporary +file.

        +

        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_io_exec.html b/docs/module_selenium-webdriver_io_exec.html new file mode 100644 index 0000000..db2facd --- /dev/null +++ b/docs/module_selenium-webdriver_io_exec.html @@ -0,0 +1,6 @@ +selenium-webdriver/io/exec
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_namespace_By.html b/docs/module_selenium-webdriver_namespace_By.html new file mode 100644 index 0000000..e01e60b --- /dev/null +++ b/docs/module_selenium-webdriver_namespace_By.html @@ -0,0 +1,56 @@ +By

        namespace By

        Alias for webdriver.By

        A collection of factory functions for creating webdriver.Locator +instances.

        +

        Functions

        className(className)code »

        Locates elements that have a specific class name. The returned locator +is equivalent to searching for elements with the CSS selector ".clazz".

        +
        Parameters
        classNamestring

        The class name to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        css(selector)code »

        Locates elements using a CSS selector. For browsers that do not support +CSS selectors, WebDriver implementations may return an +invalid selector error. An +implementation may, however, emulate the CSS selector API.

        +
        Parameters
        selectorstring

        The CSS selector to use.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        id(id)code »

        Locates an element by its ID.

        +
        Parameters
        idstring

        The ID to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        js(script, var_args)code »

        Locates an elements by evaluating a +JavaScript expression. +The result of this expression must be an element or list of elements.

        +
        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        function(webdriver.WebDriver): webdriver.promise.Promise

        A new, +JavaScript-based locator function.

        +

        linkText(text)code »

        Locates link elements whose visible +text matches the given string.

        +
        Parameters
        textstring

        The link text to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        name(name)code »

        Locates elements whose name attribute has the given value.

        +
        Parameters
        namestring

        The name attribute to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        partialLinkText(text)code »

        Locates link elements whose visible +text contains the given substring.

        +
        Parameters
        textstring

        The substring to check for in a link's visible text.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        tagName(text)code »

        Locates elements with a given tag name. The returned locator is +equivalent to using the +getElementsByTagName +DOM function.

        +
        Parameters
        textstring

        The substring to check for in a link's visible text.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        xpath(xpath)code »

        Locates elements matching a XPath selector. Care should be taken when +using an XPath selector with a webdriver.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
        xpathstring

        The XPath selector to use.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        Type Definitions

        By.Hash({className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        Short-hand expressions for the primary element locator strategies. +For example the following two statements are equivalent:

        +
        var e1 = driver.findElement(webdriver.By.id('foo'));
        +var e2 = driver.findElement({id: 'foo'});
        +
        +

        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.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_namespace_error.html b/docs/module_selenium-webdriver_namespace_error.html new file mode 100644 index 0000000..0ce22a3 --- /dev/null +++ b/docs/module_selenium-webdriver_namespace_error.html @@ -0,0 +1,4 @@ +error
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_namespace_logging.html b/docs/module_selenium-webdriver_namespace_logging.html new file mode 100644 index 0000000..20df9d6 --- /dev/null +++ b/docs/module_selenium-webdriver_namespace_logging.html @@ -0,0 +1,18 @@ +logging

        namespace logging

        Alias for webdriver.logging

        Functions

        addConsoleHandler(opt_logger)code »

        Adds the console handler to the given logger. The console handler will log +all messages using the JavaScript Console API.

        +
        Parameters
        opt_logger?webdriver.logging.Logger=

        The logger to add the handler to; defaults +to the root logger.

        +

        getLevel(arg0)code »

        Parameters
        arg0(number|string)

        getLogger(arg0)code »

        Parameters
        arg0string=

        installConsoleHandler()code »

        Installs the console log handler on the root logger.

        +

        removeConsoleHandler(opt_logger)code »

        Removes the console log handler from the given logger.

        +
        Parameters
        opt_logger?webdriver.logging.Logger=

        The logger to remove the handler from; defaults +to the root logger.

        +

        Types

        Entry

        A single log entry recorded by a WebDriver component, such as a remote +WebDriver server.

        +
        Level

        The Level class defines a set of standard logging levels that +can be used to control logging output.

        +
        LogRecord

        LogRecord objects are used to pass logging requests between +the logging framework and individual log Handlers.

        +
        Logger

        The Logger is an object used for logging debug messages.

        +
        Preferences

        Describes the log preferences for a WebDriver session.

        +
        Type

        No description.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_namespace_promise.html b/docs/module_selenium-webdriver_namespace_promise.html new file mode 100644 index 0000000..4415168 --- /dev/null +++ b/docs/module_selenium-webdriver_namespace_promise.html @@ -0,0 +1,163 @@ +promise

        namespace promise

        Alias for webdriver.promise

        Functions

        <T> all(arr)code »

        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.

        +
        Parameters
        arrArray<(T|webdriver.promise.Promise<T>)>

        An array of +promises to wait on.

        +
        Returns
        webdriver.promise.Promise<Array<T>>

        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.

        +

        asap(value, callback, opt_errback)code »

        Invokes the appropriate callback function as soon as a promised +value is resolved. This function is similar to +webdriver.promise.when, except it does not return a new promise.

        +
        Parameters
        value*

        The value to observe.

        +
        callbackFunction

        The function to call when the value is +resolved successfully.

        +
        opt_errback?Function=

        The function to call when the value is +rejected.

        +

        captureStackTrace(name, msg, topFn)code »

        Generates an error to capture the current stack trace.

        +
        Parameters
        namestring

        Error name for this stack trace.

        +
        msgstring

        Message to record.

        +
        topFnFunction

        The function that should appear at the top of the +stack; only applicable in V8.

        +
        Returns
        Error

        The generated error.

        +

        checkedNodeCall(fn, var_args)code »

        Wraps a function that expects a node-style callback as its final +argument. This callback expects two arguments: an error value (which will be +null if the call succeeded), and the success value as the second argument. +The callback will the resolve or reject the returned promise, based on its arguments.

        +
        Parameters
        fnFunction

        The function to wrap.

        +
        var_args...?

        The arguments to apply to the function, excluding the +final callback.

        +
        Returns
        webdriver.promise.Promise

        A promise that will be resolved with the +result of the provided function's callback.

        +

        consume(generatorFn, opt_self, var_args)code »

        Consumes a GeneratorFunction. Each time the generator yields a +promise, this function will wait for it to be fulfilled before feeding the +fulfilled value back into next. Likewise, if a yielded promise is +rejected, the rejection error will be passed to throw.

        +

        Example 1: the Fibonacci Sequence.

        +
        promise.consume(function* fibonacci() {
        +  var n1 = 1, n2 = 1;
        +  for (var i = 0; i < 4; ++i) {
        +    var tmp = yield n1 + n2;
        +    n1 = n2;
        +    n2 = tmp;
        +  }
        +  return n1 + n2;
        +}).then(function(result) {
        +  console.log(result);  // 13
        +});
        +
        +

        Example 2: a generator that throws.

        +
        promise.consume(function* () {
        +  yield promise.delayed(250).then(function() {
        +    throw Error('boom');
        +  });
        +}).thenCatch(function(e) {
        +  console.log(e.toString());  // Error: boom
        +});
        +
        +
        Parameters
        generatorFnFunction

        The generator function to execute.

        +
        opt_self?Object=

        The object to use as "this" when invoking the +initial generator.

        +
        var_args...*

        Any arguments to pass to the initial generator.

        +
        Returns
        webdriver.promise.Promise<?>

        A promise that will resolve to the +generator's final result.

        +
        Throws
        TypeError

        If the given function is not a generator.

        +

        controlFlow()code »

        Returns
        webdriver.promise.ControlFlow

        The currently active control flow.

        +

        createFlow(callback)code »

        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.

        +
        Parameters
        callbackfunction(webdriver.promise.ControlFlow): ?

        The entry point +to the newly created flow.

        +
        Returns
        webdriver.promise.Promise

        A promise that resolves to the callback +result.

        +

        <T> defer()code »

        Creates a new deferred object.

        +
        Returns
        webdriver.promise.Deferred<T>

        The new deferred object.

        +

        delayed(ms)code »

        Creates a promise that will be resolved at a set time in the future.

        +
        Parameters
        msnumber

        The amount of time, in milliseconds, to wait before +resolving the promise.

        +
        Returns
        webdriver.promise.Promise

        The promise.

        +

        <TYPE, SELF> filter(arr, fn, opt_self)code »

        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.

        +
        Parameters
        arr(Array<TYPE>|webdriver.promise.Promise<Array<TYPE>>)

        The +array to iterator over, or a promise that will resolve to said array.

        +
        fnfunction(this: SELF, TYPE, number, Array<TYPE>): ?(boolean|webdriver.promise.Promise<boolean>)

        The function +to call for each element in the array.

        +
        opt_self?SELF=

        The object to be used as the value of 'this' within +fn.

        +

        <T> fulfilled(opt_value)code »

        Creates a promise that has been resolved with the given value.

        +
        Parameters
        opt_value?T=

        The resolved value.

        +
        Returns
        webdriver.promise.Promise<T>

        The resolved promise.

        +

        fullyResolved(value)code »

        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 value and not a copy.

        +

        Warning: This function makes no checks against objects that contain +cyclical references:

        +
        var value = {};
        +value['self'] = value;
        +promise.fullyResolved(value);  // Stack overflow.
        +
        +
        Parameters
        value*

        The value to fully resolve.

        +
        Returns
        webdriver.promise.Promise

        A promise for a fully resolved version +of the input value.

        +

        isGenerator(fn)code »

        Tests is a function is a generator.

        +
        Parameters
        fnFunction

        The function to test.

        +
        Returns
        boolean

        Whether the function is a generator.

        +

        isPromise(value)code »

        Determines whether a value should be treated as a promise. +Any object whose "then" property is a function will be considered a promise.

        +
        Parameters
        value*

        The value to test.

        +
        Returns
        boolean

        Whether the value is a promise.

        +

        <TYPE, SELF> map(arr, fn, opt_self)code »

        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.

        +
        Parameters
        arr(Array<TYPE>|webdriver.promise.Promise<Array<TYPE>>)

        The +array to iterator over, or a promise that will resolve to said array.

        +
        fnfunction(this: SELF, TYPE, number, Array<TYPE>): ?

        The +function to call for each element in the array. This function should +expect three arguments (the element, the index, and the array itself.

        +
        opt_self?SELF=

        The object to be used as the value of 'this' within +fn.

        +

        <T> rejected(opt_reason)code »

        Creates a promise that has been rejected with the given reason.

        +
        Parameters
        opt_reason*=

        The rejection reason; may be any value, but is +usually an Error or a string.

        +
        Returns
        webdriver.promise.Promise<T>

        The rejected promise.

        +

        setDefaultFlow(flow)code »

        Changes the default flow to use when no others are active.

        +
        Parameters
        flowwebdriver.promise.ControlFlow

        The new default flow.

        +
        Throws
        Error

        If the default flow is not currently active.

        +

        when(value, opt_callback, opt_errback)code »

        Registers an observer on a promised value, 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.

        +
        Parameters
        value*

        The value to observe.

        +
        opt_callback?Function=

        The function to call when the value is +resolved successfully.

        +
        opt_errback?Function=

        The function to call when the value is +rejected.

        +
        Returns
        webdriver.promise.Promise

        A new promise.

        +

        Compiler Constants

        LONG_STACK_TRACESboolean

        Whether to append traces of then to rejection +errors.

        +

        Types

        CancellationError

        Error used when the computation of a promise is cancelled.

        +
        ControlFlow

        Handles the execution of scheduled tasks, each of which may be an +asynchronous operation.

        +
        Deferred

        Represents a value that will be resolved at some point in the future.

        +
        MultipleUnhandledRejectionError

        Error used when there are multiple unhandled promise rejections detected +within a task or callback.

        +
        Promise

        Represents the eventual value of a completed operation.

        +
        Thenable

        No description.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_namespace_stacktrace.html b/docs/module_selenium-webdriver_namespace_stacktrace.html new file mode 100644 index 0000000..c947732 --- /dev/null +++ b/docs/module_selenium-webdriver_namespace_stacktrace.html @@ -0,0 +1,17 @@ +stacktrace

        namespace stacktrace

        Alias for webdriver.stacktrace

        Functions

        format(error)code »

        Formats an error's stack trace.

        +
        Parameters
        errorError

        The error to format.

        +
        Returns
        Error

        The formatted error.

        +

        get()code »

        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.

        +
        Returns
        Array<webdriver.stacktrace.Frame>

        The frames of the stack trace.

        +

        getStack(error)code »

        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.

        +
        Parameters
        errorError

        The error.

        +
        Returns
        string

        The stack trace string.

        +

        Properties

        BROWSER_SUPPORTEDboolean

        Whether the current browser supports stack traces.

        +

        Types

        Frame

        Class representing one stack frame.

        +
        Snapshot

        Stores a snapshot of the stack trace at the time this instance was created.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_namespace_until.html b/docs/module_selenium-webdriver_namespace_until.html new file mode 100644 index 0000000..c42c0fb --- /dev/null +++ b/docs/module_selenium-webdriver_namespace_until.html @@ -0,0 +1,82 @@ +until

        namespace until

        Alias for webdriver.until

        Functions

        ableToSwitchToFrame(frame)code »

        Creates a condition that will wait until the input driver is able to switch +to the designated frame. The target frame may be specified as

        +
        1. a numeric index into +window.frames +for the currently selected frame.
        2. a webdriver.WebElement, which must reference a FRAME or IFRAME +element on the current page.
        3. a locator which may be used to first locate a FRAME or IFRAME on the +current page before attempting to switch to it.
        +

        Upon successful resolution of this condition, the driver will be left +focused on the new frame.

        +
        Parameters
        frame(number|webdriver.WebElement|webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The frame identifier.

        +
        Returns
        webdriver.until.Condition<boolean>

        A new condition.

        +

        alertIsPresent()code »

        Creates a condition that waits for an alert to be opened. Upon success, the +returned promise will be fulfilled with the handle for the opened alert.

        +
        Returns
        webdriver.until.Condition<webdriver.Alert>

        The new condition.

        +

        elementIsDisabled(element)code »

        Creates a condition that will wait for the given element to be disabled.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsEnabled(element)code »

        Creates a condition that will wait for the given element to be enabled.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsNotSelected(element)code »

        Creates a condition that will wait for the given element to be deselected.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsNotVisible(element)code »

        Creates a condition that will wait for the given element to be in the DOM, +yet not visible to the user.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsSelected(element)code »

        Creates a condition that will wait for the given element to be selected.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsVisible(element)code »

        Creates a condition that will wait for the given element to become visible.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementLocated(locator)code »

        Creates a condition that will loop until an element is +found with the given locator.

        +
        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +to use.

        +

        elementTextContains(element, substr)code »

        Creates a condition that will wait for the given element's +visible text to contain the given +substring.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        substrstring

        The substring to search for.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementTextIs(element, text)code »

        Creates a condition that will wait for the given element's +visible text to match the given +text exactly.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        textstring

        The expected text.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementTextMatches(element, regex)code »

        Creates a condition that will wait for the given element's +visible text to match a regular +expression.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        regexRegExp

        The regular expression to test against.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementsLocated(locator)code »

        Creates a condition that will loop until at least one element is +found with the given locator.

        +
        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +to use.

        +

        stalenessOf(element)code »

        Creates a condition that will wait for the given element to become stale. An +element is considered stale once it is removed from the DOM, or a new page +has loaded.

        +
        Parameters
        elementwebdriver.WebElement

        The element that should become stale.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        titleContains(substr)code »

        Creates a condition that will wait for the current page's title to contain +the given substring.

        +
        Parameters
        substrstring

        The substring that should be present in the page +title.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        titleIs(title)code »

        Creates a condition that will wait for the current page's title to match the +given value.

        +
        Parameters
        titlestring

        The expected page title.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        titleMatches(regex)code »

        Creates a condition that will wait for the current page's title to match the +given regular expression.

        +
        Parameters
        regexRegExp

        The regular expression to test against.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        Types

        Condition

        Defines a condition to

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_net.html b/docs/module_selenium-webdriver_net.html index 128d586..bef67ce 100644 --- a/docs/module_selenium-webdriver_net.html +++ b/docs/module_selenium-webdriver_net.html @@ -1 +1,7 @@ -selenium-webdriver/net

        Module selenium-webdriver/net

        code »
        Show:

        Functions

        code »getAddress ( opt_family )string

        Retrieves the external IP address for this host.

        Parameters
        opt_family: string=
        The IP family to retrieve. Defaults to "IPv4".
        Returns
        The IP address or undefined if not available.

        Retrieves a loopback address for this machine.

        Parameters
        opt_family: string=
        The IP family to retrieve. Defaults to "IPv4".
        Returns
        The IP address or undefined if not available.
        \ No newline at end of file +selenium-webdriver/net

        module selenium-webdriver/net

        Functions

        getAddress(opt_family)code »

        Retrieves the external IP address for this host.

        +
        Parameters
        opt_familystring=

        The IP family to retrieve. Defaults to "IPv4".

        +
        Returns
        string

        The IP address or undefined if not available.

        +

        getLoopbackAddress(opt_family)code »

        Retrieves a loopback address for this machine.

        +
        Parameters
        opt_familystring=

        The IP family to retrieve. Defaults to "IPv4".

        +
        Returns
        string

        The IP address or undefined if not available.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_net_portprober.html b/docs/module_selenium-webdriver_net_portprober.html index 0ae96a6..ba82679 100644 --- a/docs/module_selenium-webdriver_net_portprober.html +++ b/docs/module_selenium-webdriver_net_portprober.html @@ -1,6 +1,12 @@ -selenium-webdriver/net/portprober

        Module selenium-webdriver/net/portprober

        code »
        Show:

        Functions

        Parameters
        opt_host: string=
        The bound host to test the 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.

        Tests if a port is free.

        Parameters
        port: number
        The port to test.
        opt_host: string=
        The bound host to test the port against. - Defaults to INADDR_ANY.
        Returns
        A promise that will resolve - with whether the port is free.
        \ No newline at end of file +selenium-webdriver/net/portprober

        module selenium-webdriver/net/portprober

        Functions

        findFreePort(opt_host)code »

        Parameters
        opt_hoststring=

        The bound host to test the port against. +Defaults to INADDR_ANY.

        +
        Returns
        webdriver.promise.Promise<number>

        A promise that will resolve +to a free port. If a port cannot be found, the promise will be +rejected.

        +

        isFree(port, opt_host)code »

        Tests if a port is free.

        +
        Parameters
        portnumber

        The port to test.

        +
        opt_hoststring=

        The bound host to test the port against. +Defaults to INADDR_ANY.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the port is free.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_opera.html b/docs/module_selenium-webdriver_opera.html new file mode 100644 index 0000000..0a3a33b --- /dev/null +++ b/docs/module_selenium-webdriver_opera.html @@ -0,0 +1,62 @@ +selenium-webdriver/opera

        module selenium-webdriver/opera

        Defines a WebDriver client for the +Opera web browser (v26+). Before using this module, you must download the +latest OperaDriver +release and +ensure it can be found on your system +PATH.

        +

        There are three primary classes exported by this module:

        +
        1. +

          ServiceBuilder: configures the +remote.DriverService +that manages the +OperaDriver +child process.

          +
        2. +

          Options: defines configuration options for each new Opera +session, such as which proxy to use, +what extensions to install, or +what command-line switches to use when +starting the browser.

          +
        3. +

          Driver: the WebDriver client; each new instance will control +a unique browser session with a clean user profile (unless otherwise +configured through the Options class).

          +
        +

        By default, every Opera session will use a single driver service, which is +started the first time a Driver instance is created and terminated +when this process exits. The default service will inherit its environment +from the current process and direct all output to /dev/null. You may obtain +a handle to this default service using +getDefaultService() and change its configuration +with setDefaultService().

        +

        You may also create a Driver with its own driver service. This is +useful if you need to capture the server's log output for a specific session:

        +
        var opera = require('selenium-webdriver/opera');
        +
        +var service = new opera.ServiceBuilder()
        +    .loggingTo('/my/log/file.txt')
        +    .enableVerboseLogging()
        +    .build();
        +
        +var options = new opera.Options();
        +// configure browser options ...
        +
        +var driver = new opera.Driver(options, service);
        +
        +

        Users should only instantiate the Driver class directly when they +need a custom driver service configuration (as shown above). For normal +operation, users should start Opera using the +selenium-webdriver.Builder.

        +

        Functions

        getDefaultService()code »

        Returns the default OperaDriver service. If such a service has not been +configured, one will be constructed using the default configuration for +a OperaDriver executable found on the system PATH.

        +
        Returns
        DriverService

        The default OperaDriver service.

        +

        setDefaultService(service)code »

        Sets the default service to use for new OperaDriver instances.

        +
        Parameters
        serviceDriverService

        The service to use.

        +
        Throws
        Error

        If the default service is currently running.

        +

        Types

        Driver

        Creates a new WebDriver client for Opera.

        +
        Options

        Class for managing OperaDriver specific options.

        +
        ServiceBuilder

        Creates remote.DriverService instances that manages an +OperaDriver +server in a child process.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_opera_class_Driver.html b/docs/module_selenium-webdriver_opera_class_Driver.html new file mode 100644 index 0000000..a559e32 --- /dev/null +++ b/docs/module_selenium-webdriver_opera_class_Driver.html @@ -0,0 +1,276 @@ +Driver

        class Driver

        webdriver.WebDriver
        +  └ Driver

        Creates a new WebDriver client for Opera.

        +

        new Driver(opt_config, opt_service, opt_flow)

        Parameters
        opt_config?(Capabilities|Options)=

        The configuration +options.

        +
        opt_service?DriverService=

        The session to use; will use +the default service by default.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The control flow to use, or +null to use the currently active flow.

        +

        Instance Methods

        actions()code »

        Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

        +
        driver.actions().
        +    mouseDown(element1).
        +    mouseMove(element2).
        +    mouseUp().
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.ActionSequence

        A new action sequence for this instance.

        +

        <T> call(fn, opt_scope, var_args)code »

        Schedules a command to execute a custom function.

        +

        Defined by: webdriver.WebDriver

        Parameters
        fnfunction(...?): (T|webdriver.promise.Promise<T>)

        The function to +execute.

        +
        opt_scope?Object=

        The object in whose scope to execute the function.

        +
        var_args...*

        Any arguments to pass to the function.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved' +with the function's result.

        +

        close()code »

        Schedules a command to close the current window.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when this command has completed.

        +

        controlFlow()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.ControlFlow

        The control flow used by this +instance.

        +

        <T> executeAsyncScript(script, var_args)code »

        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 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 #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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Example #1: Performing a sleep that is synchronized with the currently +selected window:

        +
        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 #2: Synchronizing a test with an AJAX application:

        +
        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');
        +
        +

        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.

        +
        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.responseText);
        +    }
        +  }
        +  xhr.send('');
        +}).then(function(str) {
        +  console.log(JSON.parse(str)['food']);
        +});
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        <T> executeScript(script, var_args)code »

        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 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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        findElement(locator)code »

        Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator to use.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedule a command to search for multiple elements on the page.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        get(url)code »

        Schedules a command to navigate to the given URL.

        +

        Defined by: webdriver.WebDriver

        Parameters
        urlstring

        The fully qualified URL to open.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the document has finished loading.

        +

        getAllWindowHandles()code »

        Schedules a command to retrieve the current list of available window handles.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<Array<string>>

        A promise that will +be resolved with an array of window handles.

        +

        getCapabilities()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Capabilities>

        A promise +that will resolve with the this instance's capabilities.

        +

        getCurrentUrl()code »

        Schedules a command to retrieve the URL of the current page.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current URL.

        +

        getPageSource()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page source.

        +

        getSession()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Session>

        A promise for this +client's session.

        +

        getTitle()code »

        Schedules a command to retrieve the current page's title.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page's title.

        +

        getWindowHandle()code »

        Schedules a command to retrieve they current window handle.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current window handle.

        +

        isElementPresent(locatorOrElement)code »

        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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator to use, or the actual +DOM element to be located by the server.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the element is present on the page.

        +

        manage()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.Options

        The options interface for this +instance.

        +


        quit()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the command has completed.

        +

        <T> schedule(command, description)code »

        Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

        +

        Defined by: webdriver.WebDriver

        Parameters
        commandwebdriver.Command

        The command to schedule.

        +
        descriptionstring

        A description of the command for debugging.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved +with the command result.

        +

        setFileDetector(detector)code »

        This function is a no-op as file detectors are not supported by this +implementation.

        +

        Overrides: webdriver.WebDriver

        Parameters
        detectorwebdriver.FileDetector

        The detector to use or null.

        +

        sleep(ms)code »

        Schedules a command to make the driver sleep for the given amount of time.

        +

        Defined by: webdriver.WebDriver

        Parameters
        msnumber

        The amount of time, in milliseconds, to sleep.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the sleep has finished.

        +

        switchTo()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.TargetLocator

        The target locator interface for +this instance.

        +

        takeScreenshot()code »

        Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

        +
        1. Entire page +
        2. Current window +
        3. Visible portion of the current frame +
        4. The screenshot of the entire display containing the browser +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

        +

        touchActions()code »

        Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

        +
        driver.touchActions().
        +    tap(element1).
        +    doubleTap(element2).
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.TouchSequence

        A new touch sequence for this instance.

        +

        <T> wait(condition, opt_timeout, opt_message)code »

        Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

        +

        For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

        +

        Example: waiting up to 10 seconds for an element to be present and visible +on the page.

        +
        var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        +button.click();
        +
        +

        This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

        +

        Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

        +
        var started = startTestServer();
        +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        +driver.get(getServerUrl());
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

        The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

        +
        opt_timeoutnumber=

        How long to wait for the condition to be true.

        +
        opt_messagestring=

        An optional message to use if the wait times +out.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_opera_class_Options.html b/docs/module_selenium-webdriver_opera_class_Options.html new file mode 100644 index 0000000..c325dbd --- /dev/null +++ b/docs/module_selenium-webdriver_opera_class_Options.html @@ -0,0 +1,44 @@ +Options

        class Options

        webdriver.Serializable
        +  └ Options

        Class for managing OperaDriver specific options.

        +

        new Options()

        Parameters
        None.

        Instance Methods

        addArguments(var_args)code »

        Add additional command line arguments to use when launching the Opera +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".

        +
        Parameters
        var_args...(string|Array<string>)

        The arguments to add.

        +
        Returns
        Options

        A self reference.

        +

        addExtensions(var_args)code »

        Add additional extensions to install when launching Opera. Each extension +should be specified as the path to the packed CRX file, or a Buffer for an +extension.

        +
        Parameters
        var_args...(string|Buffer|Array<(string|Buffer)>)

        The +extensions to add.

        +
        Returns
        Options

        A self reference.

        +

        serialize()code »

        Converts this instance to its JSON wire protocol representation. Note this +function is an implementation not intended for general use.

        +

        Overrides: webdriver.Serializable

        Returns
        (T|IThenable<T>)
        +

        , +localState: (Object|undefined), +logPath: (string|undefined), +prefs: (Object|undefined)}} The JSON wire protocol representation +of this instance.

        +
        +

        setLoggingPrefs(prefs)code »

        Sets the logging preferences for the new session.

        +
        Parameters
        prefswebdriver.logging.Preferences

        The logging preferences.

        +
        Returns
        Options

        A self reference.

        +

        setOperaBinaryPath(path)code »

        Sets the path to the Opera binary to use. On Mac OS X, this path should +reference the actual Opera executable, not just the application binary. The +binary path be absolute or relative to the operadriver server executable, but +it must exist on the machine that will launch Opera.

        +
        Parameters
        pathstring

        The path to the Opera binary to use.

        +
        Returns
        Options

        A self reference.

        +

        setProxy(proxy)code »

        Sets the proxy settings for the new session.

        +
        Parameters
        proxyselenium-webdriver.ProxyConfig

        The proxy configuration to use.

        +
        Returns
        Options

        A self reference.

        +

        toCapabilities(opt_capabilities)code »

        Converts this options instance to a webdriver.Capabilities object.

        +
        Parameters
        opt_capabilities?Capabilities=

        The capabilities to merge +these options into, if any.

        +
        Returns
        webdriver.Capabilities

        The capabilities.

        +

        Static Functions

        Options.fromCapabilities(capabilities)code »

        Extracts the OperaDriver specific options from the given capabilities +object.

        +
        Parameters
        capabilitiesCapabilities

        The capabilities object.

        +
        Returns
        Options

        The OperaDriver options.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_opera_class_ServiceBuilder.html b/docs/module_selenium-webdriver_opera_class_ServiceBuilder.html new file mode 100644 index 0000000..12b209b --- /dev/null +++ b/docs/module_selenium-webdriver_opera_class_ServiceBuilder.html @@ -0,0 +1,35 @@ +ServiceBuilder

        class ServiceBuilder

        Creates remote.DriverService instances that manages an +OperaDriver +server in a child process.

        +

        new ServiceBuilder(opt_exe)

        Parameters
        opt_exestring=

        Path to the server executable to use. If omitted, +the builder will attempt to locate the operadriver on the current +PATH.

        +
        Throws
        Error

        If provided executable does not exist, or the operadriver +cannot be found on the PATH.

        +

        Instance Methods

        build()code »

        Creates a new DriverService using this instance's current configuration.

        +
        Returns
        DriverService

        A new driver service using this instance's +current configuration.

        +
        Throws
        Error

        If the driver exectuable was not specified and a default +could not be found on the current PATH.

        +

        enableVerboseLogging()code »

        Enables verbose logging.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        loggingTo(path)code »

        Sets the path of the log file the driver should log to. If a log file is +not specified, the driver will log to stderr.

        +
        Parameters
        pathstring

        Path of the log file to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        setStdio(config)code »

        Defines the stdio configuration for the driver service. See +child_process.spawn for more information.

        +
        Parameters
        config(string|Array<?(string|number|Stream)>)

        The +configuration to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        silent()code »

        Silence sthe drivers output.

        +
        Returns
        ServiceBuilder

        A self reference.

        +

        usingPort(port)code »

        Sets the port to start the OperaDriver on.

        +
        Parameters
        portnumber

        The port to use, or 0 for any free port.

        +
        Returns
        ServiceBuilder

        A self reference.

        +
        Throws
        Error

        If the port is invalid.

        +

        withEnvironment(env)code »

        Defines the environment to start the server under. This settings will be +inherited by every browser session started by the server.

        +
        Parameters
        envObject<string, string>

        The environment to use.

        +
        Returns
        ServiceBuilder

        A self reference.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_phantomjs.html b/docs/module_selenium-webdriver_phantomjs.html index a4fd791..a43f7d7 100644 --- a/docs/module_selenium-webdriver_phantomjs.html +++ b/docs/module_selenium-webdriver_phantomjs.html @@ -1 +1,2 @@ -selenium-webdriver/phantomjs

        Module selenium-webdriver/phantomjs

        code »
        Show:

        Functions

        code »createDriver ( opt_capabilities )!webdriver.WebDriver

        Creates a new PhantomJS WebDriver client.

        Parameters
        opt_capabilities: webdriver.Capabilities=
        The desired capabilities.
        Returns
        A new WebDriver instance.
        \ No newline at end of file +selenium-webdriver/phantomjs

        module selenium-webdriver/phantomjs

        Types

        Driver

        Creates a new WebDriver client for PhantomJS.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_phantomjs_class_Driver.html b/docs/module_selenium-webdriver_phantomjs_class_Driver.html new file mode 100644 index 0000000..69b5e42 --- /dev/null +++ b/docs/module_selenium-webdriver_phantomjs_class_Driver.html @@ -0,0 +1,299 @@ +Driver

        class Driver

        webdriver.WebDriver
        +  └ Driver

        Creates a new WebDriver client for PhantomJS.

        +

        new Driver(opt_capabilities, opt_flow)

        Parameters
        opt_capabilities?Capabilities=

        The desired capabilities.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The control flow to use, or +null to use the currently active flow.

        +

        Instance Methods

        actions()code »

        Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

        +
        driver.actions().
        +    mouseDown(element1).
        +    mouseMove(element2).
        +    mouseUp().
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.ActionSequence

        A new action sequence for this instance.

        +

        <T> call(fn, opt_scope, var_args)code »

        Schedules a command to execute a custom function.

        +

        Defined by: webdriver.WebDriver

        Parameters
        fnfunction(...?): (T|webdriver.promise.Promise<T>)

        The function to +execute.

        +
        opt_scope?Object=

        The object in whose scope to execute the function.

        +
        var_args...*

        Any arguments to pass to the function.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved' +with the function's result.

        +

        close()code »

        Schedules a command to close the current window.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when this command has completed.

        +

        controlFlow()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.ControlFlow

        The control flow used by this +instance.

        +

        <T> executeAsyncScript(script, var_args)code »

        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 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 #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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Example #1: Performing a sleep that is synchronized with the currently +selected window:

        +
        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 #2: Synchronizing a test with an AJAX application:

        +
        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');
        +
        +

        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.

        +
        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.responseText);
        +    }
        +  }
        +  xhr.send('');
        +}).then(function(str) {
        +  console.log(JSON.parse(str)['food']);
        +});
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        <T> executePhantomJS(script, var_args)code »

        Executes a PhantomJS fragment. This method is similar to +#executeScript, except it exposes the +PhantomJS API to the injected +script.

        +

        The injected script will execute in the context of PhantomJS's +page variable. If a page has not been loaded before calling this +method, one will be created.

        +

        Be sure to wrap callback definitions in a try/catch block, as failures +may cause future WebDriver calls to fail.

        +

        Certain callbacks are used by GhostDriver (the PhantomJS WebDriver +implementation) and overriding these may cause the script to fail. It is +recommended that you check for existing callbacks before defining your own. +

        +

        As with #executeScript, the injected script may be defined as +a string for an anonymous function body (e.g. "return 123;"), or as a +function. If a function is provided, it will be decompiled to its original +source. Note that injecting functions is provided as a convenience to +simplify defining complex scripts. Care must be taken that the function +only references variables that will be defined in the page's scope and +that the function does not override Function.prototype.toString +(overriding toString() will interfere with how the function is +decompiled.

        +
        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise

        A promise that resolve to the +script's return value.

        +

        <T> executeScript(script, var_args)code »

        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 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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        findElement(locator)code »

        Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator to use.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedule a command to search for multiple elements on the page.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        get(url)code »

        Schedules a command to navigate to the given URL.

        +

        Defined by: webdriver.WebDriver

        Parameters
        urlstring

        The fully qualified URL to open.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the document has finished loading.

        +

        getAllWindowHandles()code »

        Schedules a command to retrieve the current list of available window handles.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<Array<string>>

        A promise that will +be resolved with an array of window handles.

        +

        getCapabilities()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Capabilities>

        A promise +that will resolve with the this instance's capabilities.

        +

        getCurrentUrl()code »

        Schedules a command to retrieve the URL of the current page.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current URL.

        +

        getPageSource()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page source.

        +

        getSession()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Session>

        A promise for this +client's session.

        +

        getTitle()code »

        Schedules a command to retrieve the current page's title.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page's title.

        +

        getWindowHandle()code »

        Schedules a command to retrieve they current window handle.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current window handle.

        +

        isElementPresent(locatorOrElement)code »

        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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator to use, or the actual +DOM element to be located by the server.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the element is present on the page.

        +

        manage()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.Options

        The options interface for this +instance.

        +


        quit()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the command has completed.

        +

        <T> schedule(command, description)code »

        Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

        +

        Defined by: webdriver.WebDriver

        Parameters
        commandwebdriver.Command

        The command to schedule.

        +
        descriptionstring

        A description of the command for debugging.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved +with the command result.

        +

        setFileDetector(detector)code »

        This function is a no-op as file detectors are not supported by this +implementation.

        +

        Overrides: webdriver.WebDriver

        Parameters
        detectorwebdriver.FileDetector

        The detector to use or null.

        +

        sleep(ms)code »

        Schedules a command to make the driver sleep for the given amount of time.

        +

        Defined by: webdriver.WebDriver

        Parameters
        msnumber

        The amount of time, in milliseconds, to sleep.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the sleep has finished.

        +

        switchTo()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.TargetLocator

        The target locator interface for +this instance.

        +

        takeScreenshot()code »

        Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

        +
        1. Entire page +
        2. Current window +
        3. Visible portion of the current frame +
        4. The screenshot of the entire display containing the browser +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

        +

        touchActions()code »

        Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

        +
        driver.touchActions().
        +    tap(element1).
        +    doubleTap(element2).
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.TouchSequence

        A new touch sequence for this instance.

        +

        <T> wait(condition, opt_timeout, opt_message)code »

        Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

        +

        For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

        +

        Example: waiting up to 10 seconds for an element to be present and visible +on the page.

        +
        var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        +button.click();
        +
        +

        This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

        +

        Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

        +
        var started = startTestServer();
        +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        +driver.get(getServerUrl());
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

        The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

        +
        opt_timeoutnumber=

        How long to wait for the condition to be true.

        +
        opt_messagestring=

        An optional message to use if the wait times +out.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_proxy.html b/docs/module_selenium-webdriver_proxy.html index 67b9e60..ab77a0f 100644 --- a/docs/module_selenium-webdriver_proxy.html +++ b/docs/module_selenium-webdriver_proxy.html @@ -1,24 +1,28 @@ -selenium-webdriver/proxy

        Module selenium-webdriver/proxy

        code »

        Defines functions for configuring a webdriver proxy: -

        
        - var webdriver = require('selenium-webdriver'),
        -     proxy = require('selenium-webdriver/proxy');
        +selenium-webdriver/proxy

        module selenium-webdriver/proxy

        Defines functions for configuring a webdriver proxy:

        +
        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();
        - 
        Show:

        Type Definitions

        code »ProxyConfig : ({proxyType: string}|{proxyType: string, proxyAutoconfigUrl: string}|{proxyType: string, ftpProxy: string, httpProxy: string, sslProxy: string, noProxy: string})
        Proxy configuration object, as defined by the WebDriver wire protocol.

        Functions

        code »direct ( )!ProxyConfig

        Configures WebDriver to bypass all browser proxies.

        Returns
        A new proxy configuration object.
        code »manual ( options )!ProxyConfig

        Manually configures the browser proxy. The following options are - supported: -

          -
        • 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. -
        - - Behavior is undefined for FTP, HTTP, and HTTPS requests if the - corresponding key is omitted from the configuration options.
        Parameters
        options: {ftp: (string|undefined), http: (string|undefined), https: (string|undefined), bypass: (string|!Array.<string>|undefined)}
        Proxy - configuration options.
        Returns
        A new proxy configuration object.
        code »pac ( url )!ProxyConfig

        Configures WebDriver to configure the browser proxy using the PAC file at - the given URL.

        Parameters
        url: string
        URL for the PAC proxy to use.
        Returns
        A new proxy configuration object.
        code »system ( )!ProxyConfig

        Configures WebDriver to use the current system's proxy.

        Returns
        A new proxy configuration object.
        \ No newline at end of file +var driver = new webdriver.Builder() + .withCapabilities(webdriver.Capabilities.chrome()) + .setProxy(proxy.manual({http: 'host:1234'})) + .build(); + +

        Functions

        direct()code »

        Configures WebDriver to bypass all browser proxies.

        +
        Returns
        {proxyType: string}

        A new proxy configuration object.

        +

        manual(options)code »

        Manually configures the browser proxy. The following options are +supported:

        +
        • 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.
        +

        Behavior is undefined for FTP, HTTP, and HTTPS requests if the +corresponding key is omitted from the configuration options.

        +
        Parameters
        options{bypass: (string|Array<string>|undefined), ftp: (string|undefined), http: (string|undefined), https: (string|undefined)}

        Proxy +configuration options.

        +
        Returns
        {proxyType: string}

        A new proxy configuration object.

        +

        pac(url)code »

        Configures WebDriver to configure the browser proxy using the PAC file at +the given URL.

        +
        Parameters
        urlstring

        URL for the PAC proxy to use.

        +
        Returns
        {proxyType: string}

        A new proxy configuration object.

        +

        system()code »

        Configures WebDriver to use the current system's proxy.

        +
        Returns
        {proxyType: string}

        A new proxy configuration object.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_remote.html b/docs/module_selenium-webdriver_remote.html index 5b260c2..73a0e92 100644 --- a/docs/module_selenium-webdriver_remote.html +++ b/docs/module_selenium-webdriver_remote.html @@ -1,16 +1,8 @@ -selenium-webdriver/remote

        Module selenium-webdriver/remote

        code »

        Classes

        DriverService
        Manages the life and death of a native executable WebDriver server.
        SeleniumServer
        Manages the life and death of the Selenium standalone server.
        Show:

        Type Definitions

        Configuration options for a DriverService instance. -
          -
        • 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. -
        \ No newline at end of file +selenium-webdriver/remote
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_remote_class_DriverService.html b/docs/module_selenium-webdriver_remote_class_DriverService.html index df1f193..517a143 100644 --- a/docs/module_selenium-webdriver_remote_class_DriverService.html +++ b/docs/module_selenium-webdriver_remote_class_DriverService.html @@ -1,19 +1,32 @@ -DriverService

        Class DriverService

        code »

        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.

        Constructor

        DriverService ( executable, options )
        Parameters
        executable: string
        Path to the executable to run.
        options: !ServiceOptions
        Configuration options for the service.
        Show:

        Instance Methods

        Returns
        A promise that resolves to - the server's address.
        Throws
        Error
        If the server has not been started.
        Returns
        Whether the underlying service process is running.

        Stops the service if it is not currently running. This function will kill - the server immediately. To synchronize with the active control flow, use - #stop().

        Returns
        A promise that will be resolved when - the server has been stopped.

        Starts the server if it is not already running.

        Parameters
        opt_timeoutMs: number=
        How long to wait, in milliseconds, for the - server to start accepting requests. Defaults to 30 seconds.
        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.

        Schedules a task in the current control flow to stop the server if it is - currently running.

        Returns
        A promise that will be resolved when - the server has been stopped.

        Instance Properties

        Promise that resolves to the server's address or null if the server has not - been started.

        code »process_ : child_process.ChildProcess

        Promise that tracks the status of shutting down the server, or null if the - server is not currently shutting down.

        Static Properties

        The default amount of time, in milliseconds, to wait for the server to - start.

        \ No newline at end of file +DriverService

        class DriverService

        Manages the life and death of a native executable WebDriver server.

        +

        It is expected that the driver server implements the +https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol. +Furthermore, the managed server should support multiple concurrent sessions, +so that this class may be reused for multiple clients.

        +

        new DriverService(executable, options)

        Parameters
        executablestring

        Path to the executable to run.

        +
        options{args: (Array<string>|webdriver.promise.Promise), env: (Object<string, string>|undefined), loopback: (boolean|undefined), path: (string|undefined), port: (number|webdriver.promise.Promise), stdio: (string|Array<?(string|number|Stream)>|undefined)}

        Configuration options for the service.

        +

        Instance Methods

        address()code »

        Returns
        webdriver.promise.Promise

        A promise that resolves to +the server's address.

        +
        Throws
        Error

        If the server has not been started.

        +

        isRunning()code »

        Returns whether the underlying process is still running. This does not take +into account whether the process is in the process of shutting down.

        +
        Returns
        boolean

        Whether the underlying service process is running.

        +

        kill()code »

        Stops the service if it is not currently running. This function will kill +the server immediately. To synchronize with the active control flow, use +#stop().

        +
        Returns
        webdriver.promise.Promise

        A promise that will be resolved when +the server has been stopped.

        +

        start(opt_timeoutMs)code »

        Starts the server if it is not already running.

        +
        Parameters
        opt_timeoutMsnumber=

        How long to wait, in milliseconds, for the +server to start accepting requests. Defaults to 30 seconds.

        +
        Returns
        webdriver.promise.Promise

        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.

        +

        stop()code »

        Schedules a task in the current control flow to stop the server if it is +currently running.

        +
        Returns
        webdriver.promise.Promise

        A promise that will be resolved when +the server has been stopped.

        +

        Static Properties

        DriverService.DEFAULT_START_TIMEOUT_MSnumber

        The default amount of time, in milliseconds, to wait for the server to +start.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_remote_class_FileDetector.html b/docs/module_selenium-webdriver_remote_class_FileDetector.html new file mode 100644 index 0000000..032672e --- /dev/null +++ b/docs/module_selenium-webdriver_remote_class_FileDetector.html @@ -0,0 +1,22 @@ +FileDetector

        class FileDetector

        final
        webdriver.FileDetector
        +  └ FileDetector

        A webdriver.FileDetector that may be used when running +against a remote +Selenium server.

        +

        When a file path on the local machine running this script is entered with +WebElement#sendKeys, this file detector +will transfer the specified file to the Selenium server's host; the sendKeys +command will be updated to use the transfered file's path.

        +

        Note: This class depends on a non-standard command supported on the +Java Selenium server. The file detector will fail if used with a server that +only supports standard WebDriver commands (such as the ChromeDriver).

        +

        new FileDetector()

        Parameters
        None.

        Instance Methods

        handleFile(driver, path)code »

        Handles the file specified by the given path, preparing it for use with +the current browser. If the path does not refer to a valid file, it will +be returned unchanged, otherwisee a path suitable for use with the current +browser will be returned.

        +

        This default implementation is a no-op. Subtypes may override this +function for custom tailored file handling.

        +

        Overrides: webdriver.FileDetector

        Parameters
        driverwebdriver.WebDriver

        The driver for the current browser.

        +
        pathstring

        The path to process.

        +
        Returns
        webdriver.promise.Promise<string>

        A promise for the processed +file path.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_remote_class_SeleniumServer.html b/docs/module_selenium-webdriver_remote_class_SeleniumServer.html index 3d496b8..4b938b6 100644 --- a/docs/module_selenium-webdriver_remote_class_SeleniumServer.html +++ b/docs/module_selenium-webdriver_remote_class_SeleniumServer.html @@ -1,31 +1,41 @@ -SeleniumServer

        Class SeleniumServer

        code »
        DriverService
        -  └ SeleniumServer

        Manages the life and death of the Selenium standalone server. The server - may be obtained from http://selenium-release.storage.googleapis.com/index.html.

        Constructor

        SeleniumServer ( jar, options )
        Parameters
        jar: string
        Path to the Selenium server jar.
        options: !SeleniumServer.Options
        Configuration options for the - server.
        Throws
        Error
        If an invalid port is specified.
        Show:

        Type Definitions

        Options for the Selenium server: -
          -
        • 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

        Returns
        A promise that resolves to - the server's address.
        Throws
        Error
        If the server has not been started.
        Returns
        Whether the underlying service process is running.

        Stops the service if it is not currently running. This function will kill - the server immediately. To synchronize with the active control flow, use - #stop().

        Returns
        A promise that will be resolved when - the server has been stopped.

        Starts the server if it is not already running.

        Parameters
        opt_timeoutMs: number=
        How long to wait, in milliseconds, for the - server to start accepting requests. Defaults to 30 seconds.
        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.

        Schedules a task in the current control flow to stop the server if it is - currently running.

        Returns
        A promise that will be resolved when - the server has been stopped.

        Instance Properties

        Promise that resolves to the server's address or null if the server has not - been started.

        code »process_ : child_process.ChildProcess

        Promise that tracks the status of shutting down the server, or null if the - server is not currently shutting down.

        Static Properties

        \ No newline at end of file +SeleniumServer

        class SeleniumServer

        DriverService
        +  └ SeleniumServer

        Manages the life and death of the + +standalone Selenium server.

        +

        new SeleniumServer(jar, opt_options)

        Parameters
        jarstring

        Path to the Selenium server jar.

        +
        opt_options{args: (Array<string>|webdriver.promise.Promise), env: (Object<string, string>|undefined), jvmArgs: (Array<string>|webdriver.promise.Promise|undefined), loopback: (boolean|undefined), port: (number|webdriver.promise.Promise), stdio: (string|Array<?(string|number|Stream)>|undefined)}=

        Configuration options for the +server.

        +
        Throws
        Error

        If the path to the Selenium jar is not specified or if an +invalid port is specified.

        +

        Instance Methods

        address()code »

        Defined by: DriverService

        Returns
        webdriver.promise.Promise

        A promise that resolves to +the server's address.

        +
        Throws
        Error

        If the server has not been started.

        +

        isRunning()code »

        Returns whether the underlying process is still running. This does not take +into account whether the process is in the process of shutting down.

        +

        Defined by: DriverService

        Returns
        boolean

        Whether the underlying service process is running.

        +

        kill()code »

        Stops the service if it is not currently running. This function will kill +the server immediately. To synchronize with the active control flow, use +#stop().

        +

        Defined by: DriverService

        Returns
        webdriver.promise.Promise

        A promise that will be resolved when +the server has been stopped.

        +

        start(opt_timeoutMs)code »

        Starts the server if it is not already running.

        +

        Defined by: DriverService

        Parameters
        opt_timeoutMsnumber=

        How long to wait, in milliseconds, for the +server to start accepting requests. Defaults to 30 seconds.

        +
        Returns
        webdriver.promise.Promise

        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.

        +

        stop()code »

        Schedules a task in the current control flow to stop the server if it is +currently running.

        +

        Defined by: DriverService

        Returns
        webdriver.promise.Promise

        A promise that will be resolved when +the server has been stopped.

        +

        Type Definitions

        SeleniumServer.Options{args: (Array<string>|webdriver.promise.Promise), env: (Object<string, string>|undefined), jvmArgs: (Array<string>|webdriver.promise.Promise|undefined), loopback: (boolean|undefined), port: (number|webdriver.promise.Promise), stdio: (string|Array<?(string|number|Stream)>|undefined)}

        Options for the Selenium server:

        +
        • loopback - Whether the server 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.
        • 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.
        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_safari.html b/docs/module_selenium-webdriver_safari.html new file mode 100644 index 0000000..a4c1235 --- /dev/null +++ b/docs/module_selenium-webdriver_safari.html @@ -0,0 +1,9 @@ +selenium-webdriver/safari

        module selenium-webdriver/safari

        Defines a WebDriver client for Safari. Before using this +module, you must install the +latest version +of the SafariDriver browser extension; using Safari for normal browsing is +not recommended once the extension has been installed. You can, and should, +disable the extension when the browser is not being used with WebDriver.

        +

        Types

        Driver

        A WebDriver client for Safari.

        +
        Options

        Configuration options specific to the SafariDriver.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_safari_class_Driver.html b/docs/module_selenium-webdriver_safari_class_Driver.html new file mode 100644 index 0000000..4ca43e0 --- /dev/null +++ b/docs/module_selenium-webdriver_safari_class_Driver.html @@ -0,0 +1,279 @@ +Driver

        class Driver

        webdriver.WebDriver
        +  └ Driver

        A WebDriver client for Safari. This class should never be instantiated +directly; instead, use the selenium-webdriver.Builder:

        +
        var driver = new Builder()
        +    .forBrowser('safari')
        +    .build();
        +
        +

        new Driver(opt_config, opt_flow)

        Parameters
        opt_config?(Options|Capabilities)=

        The configuration +options for the new session.

        +
        opt_flow?webdriver.promise.ControlFlow=

        The control flow to create +the driver under.

        +

        Instance Methods

        actions()code »

        Creates a new action sequence using this driver. The sequence will not be +scheduled for execution until webdriver.ActionSequence#perform is +called. Example:

        +
        driver.actions().
        +    mouseDown(element1).
        +    mouseMove(element2).
        +    mouseUp().
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.ActionSequence

        A new action sequence for this instance.

        +

        <T> call(fn, opt_scope, var_args)code »

        Schedules a command to execute a custom function.

        +

        Defined by: webdriver.WebDriver

        Parameters
        fnfunction(...?): (T|webdriver.promise.Promise<T>)

        The function to +execute.

        +
        opt_scope?Object=

        The object in whose scope to execute the function.

        +
        var_args...*

        Any arguments to pass to the function.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved' +with the function's result.

        +

        close()code »

        Schedules a command to close the current window.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when this command has completed.

        +

        controlFlow()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.ControlFlow

        The control flow used by this +instance.

        +

        <T> executeAsyncScript(script, var_args)code »

        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 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 #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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Example #1: Performing a sleep that is synchronized with the currently +selected window:

        +
        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 #2: Synchronizing a test with an AJAX application:

        +
        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');
        +
        +

        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.

        +
        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.responseText);
        +    }
        +  }
        +  xhr.send('');
        +}).then(function(str) {
        +  console.log(JSON.parse(str)['food']);
        +});
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        <T> executeScript(script, var_args)code »

        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 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:

        +
        • For a HTML element, the value will resolve to a +webdriver.WebElement
        • Null and undefined return values will resolve to null
        • Booleans, numbers, and strings will resolve as is
        • Functions will resolve to their string representation
        • For arrays and objects, each member item will be converted according to +the rules above
        +

        Defined by: webdriver.WebDriver

        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will resolve to the +scripts return value.

        +

        findElement(locator)code »

        Schedule a command to find an element on the page. If the element cannot be +found, a bot.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.

        +

        The search criteria for an element may be defined using one of the +factories in the 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'});
        +
        +

        You may also provide a custom locator function, which takes as input +this WebDriver instance and returns a 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];
        +  });
        +}
        +
        +

        When running in the browser, a WebDriver cannot manipulate DOM elements +directly; it may do so only through a 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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The +locator to use.

        +
        Returns
        webdriver.WebElement

        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.

        +

        findElements(locator)code »

        Schedule a command to search for multiple elements on the page.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +strategy to use when searching for the element.

        +
        Returns
        webdriver.promise.Promise<Array<webdriver.WebElement>>

        A +promise that will resolve to an array of WebElements.

        +

        get(url)code »

        Schedules a command to navigate to the given URL.

        +

        Defined by: webdriver.WebDriver

        Parameters
        urlstring

        The fully qualified URL to open.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the document has finished loading.

        +

        getAllWindowHandles()code »

        Schedules a command to retrieve the current list of available window handles.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<Array<string>>

        A promise that will +be resolved with an array of window handles.

        +

        getCapabilities()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Capabilities>

        A promise +that will resolve with the this instance's capabilities.

        +

        getCurrentUrl()code »

        Schedules a command to retrieve the URL of the current page.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current URL.

        +

        getPageSource()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page source.

        +

        getSession()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<webdriver.Session>

        A promise for this +client's session.

        +

        getTitle()code »

        Schedules a command to retrieve the current page's title.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current page's title.

        +

        getWindowHandle()code »

        Schedules a command to retrieve they current window handle.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved with the current window handle.

        +

        isElementPresent(locatorOrElement)code »

        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.

        +

        Defined by: webdriver.WebDriver

        Parameters
        locatorOrElement(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator to use, or the actual +DOM element to be located by the server.

        +
        Returns
        webdriver.promise.Promise<boolean>

        A promise that will resolve +with whether the element is present on the page.

        +

        manage()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.Options

        The options interface for this +instance.

        +


        quit()code »

        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.

        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the command has completed.

        +

        <T> schedule(command, description)code »

        Schedules a webdriver.Command to be executed by this driver's +webdriver.CommandExecutor.

        +

        Defined by: webdriver.WebDriver

        Parameters
        commandwebdriver.Command

        The command to schedule.

        +
        descriptionstring

        A description of the command for debugging.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be resolved +with the command result.

        +

        setFileDetector(detector)code »

        Sets the file detector that should be +used with this instance.

        +

        Defined by: webdriver.WebDriver

        Parameters
        detectorwebdriver.FileDetector

        The detector to use or null.

        +

        sleep(ms)code »

        Schedules a command to make the driver sleep for the given amount of time.

        +

        Defined by: webdriver.WebDriver

        Parameters
        msnumber

        The amount of time, in milliseconds, to sleep.

        +
        Returns
        webdriver.promise.Promise<undefined>

        A promise that will be resolved +when the sleep has finished.

        +

        switchTo()code »

        Defined by: webdriver.WebDriver

        Returns
        webdriver.WebDriver.TargetLocator

        The target locator interface for +this instance.

        +

        takeScreenshot()code »

        Schedule a command to take a screenshot. The driver makes a best effort to +return a screenshot of the following, in order of preference:

        +
        1. Entire page +
        2. Current window +
        3. Visible portion of the current frame +
        4. The screenshot of the entire display containing the browser +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.promise.Promise<string>

        A promise that will be +resolved to the screenshot as a base-64 encoded PNG.

        +

        touchActions()code »

        Creates a new touch sequence using this driver. The sequence will not be +scheduled for execution until webdriver.TouchSequence#perform is +called. Example:

        +
        driver.touchActions().
        +    tap(element1).
        +    doubleTap(element2).
        +    perform();
        +
        +

        Defined by: webdriver.WebDriver

        Returns
        webdriver.TouchSequence

        A new touch sequence for this instance.

        +

        <T> wait(condition, opt_timeout, opt_message)code »

        Schedules a command to wait for a condition to hold. The condition may be +specified by a webdriver.until.Condition, as a custom function, or +as a webdriver.promise.Promise.

        +

        For a webdriver.until.Condition or function, the wait will repeatedly +evaluate the condition until it returns a truthy value. If any errors occur +while evaluating the condition, they will be allowed to propagate. In the +event a condition returns a promise, the +polling loop will wait for it to be resolved and use the resolved value for +whether the condition has been satisified. Note the resolution time for +a promise is factored into whether a wait has timed out.

        +

        Example: waiting up to 10 seconds for an element to be present and visible +on the page.

        +
        var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        +button.click();
        +
        +

        This function may also be used to block the command flow on the resolution +of a promise. When given a promise, the +command will simply wait for its resolution before completing. A timeout may +be provided to fail the command if the promise does not resolve before the +timeout expires.

        +

        Example: Suppose you have a function, startTestServer, that returns a +promise for when a server is ready for requests. You can block a WebDriver +client on this promise with:

        +
        var started = startTestServer();
        +driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        +driver.get(getServerUrl());
        +
        +

        Defined by: webdriver.WebDriver

        Parameters
        condition(webdriver.promise.Promise<T>|webdriver.until.Condition<T>|function(webdriver.WebDriver): T)

        The condition to +wait on, defined as a promise, condition object, or a function to +evaluate as a condition.

        +
        opt_timeoutnumber=

        How long to wait for the condition to be true.

        +
        opt_messagestring=

        An optional message to use if the wait times +out.

        +
        Returns
        webdriver.promise.Promise<T>

        A promise that will be fulfilled +with the first truthy value returned by the condition function, or +rejected if the condition times out.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_safari_class_Options.html b/docs/module_selenium-webdriver_safari_class_Options.html new file mode 100644 index 0000000..d725006 --- /dev/null +++ b/docs/module_selenium-webdriver_safari_class_Options.html @@ -0,0 +1,23 @@ +Options

        class Options

        webdriver.Serializable
        +  └ Options

        Configuration options specific to the SafariDriver.

        +

        new Options()

        Parameters
        None.

        Instance Methods

        serialize()code »

        Converts this instance to its JSON wire protocol representation. Note this +function is an implementation detail not intended for general use.

        +

        Overrides: webdriver.Serializable

        Returns
        Object<string, *>

        The JSON wire protocol representation of this +instance.

        +

        setCleanSession(clean)code »

        Sets whether to force Safari to start with a clean session. Enabling this +option will cause all global browser data to be deleted.

        +
        Parameters
        cleanboolean

        Whether to make sure the session has no cookies, +cache entries, local storage, or databases.

        +
        Returns
        Options

        A self reference.

        +

        setLoggingPrefs(prefs)code »

        Sets the logging preferences for the new session.

        +
        Parameters
        prefswebdriver.logging.Preferences

        The logging preferences.

        +
        Returns
        Options

        A self reference.

        +

        toCapabilities(opt_capabilities)code »

        Converts this options instance to a webdriver.Capabilities object.

        +
        Parameters
        opt_capabilities?Capabilities=

        The capabilities to merge +these options into, if any.

        +
        Returns
        webdriver.Capabilities

        The capabilities.

        +

        Static Functions

        Options.fromCapabilities(capabilities)code »

        Extracts the SafariDriver specific options from the given capabilities +object.

        +
        Parameters
        capabilitiesCapabilities

        The capabilities object.

        +
        Returns
        Options

        The ChromeDriver options.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_testing.html b/docs/module_selenium-webdriver_testing.html index 336a5bb..bb12638 100644 --- a/docs/module_selenium-webdriver_testing.html +++ b/docs/module_selenium-webdriver_testing.html @@ -1,72 +1,74 @@ -selenium-webdriver/testing

        Module selenium-webdriver/testing

        code »

        Provides wrappers around the following global functions from - Mocha's BDD interface: -

          -
        • after -
        • afterEach -
        • before -
        • beforeEach -
        • it -
        • it.only -
        • it.skip -
        • xit -
        +selenium-webdriver/testing

        module selenium-webdriver/testing

        Provides wrappers around the following global functions from +Mocha's BDD interface:

        +
        • after
        • afterEach
        • before
        • beforeEach
        • it
        • it.only
        • it.skip
        • xit
        +

        The provided wrappers leverage the webdriver.promise.ControlFlow +to simplify writing asynchronous tests:

        +
        var By = require('selenium-webdriver').By,
        +    until = require('selenium-webdriver').until,
        +    firefox = require('selenium-webdriver/firefox'),
        +    test = require('selenium-webdriver/testing');
         
        - 

        The provided wrappers leverage the webdriver.promise.ControlFlow to - simplify writing asynchronous tests: -

        
        - var webdriver = require('selenium-webdriver'),
        -     remote = require('selenium-webdriver/remote'),
        -     test = require('selenium-webdriver/testing');
        +test.describe('Google Search', function() {
        +  var driver;
         
        - test.describe('Google Search', function() {
        -   var driver, server;
        +  test.before(function() {
        +    driver = new firefox.Driver();
        +  });
         
        -   test.before(function() {
        -     server = new remote.SeleniumServer({
        -       jar: 'path/to/selenium-server-standalone.jar'
        -     });
        -     server.start();
        +  test.after(function() {
        +    driver.quit();
        +  });
         
        -     driver = new webdriver.Builder().
        -         withCapabilities({'browserName': 'firefox'}).
        -         usingServer(server.address()).
        -         build();
        -   });
        +  test.it('should append query to title', function() {
        +    driver.get('http://www.google.com/ncr');
        +    driver.findElement(By.name('q')).sendKeys('webdriver');
        +    driver.findElement(By.name('btnG')).click();
        +    driver.wait(until.titleIs('webdriver - Google Search'), 1000);
        +  });
        +});
        +
        +

        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:

        +
        test.ignore(maybe()).it('is flaky', function() {
        +  if (Math.random() < 0.5) throw Error();
        +});
         
        -   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');
        -   });
        - });
        - 
        - -

        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: -

        
        -   test.ignore(maybe()).it('is flaky', function() {
        -     if (Math.random() < 0.5) throw Error();
        -   });
        -
        -   function maybe() { return Math.random() < 0.5; }
        - 
        Show:

        Functions

        Register a function to call after the current suite finishes.

        Parameters
        fn: function()
        .

        Register a function to call after each test in a suite.

        Parameters
        fn: function()
        .

        Register a function to call before the current suite starts.

        Parameters
        fn: function()
        .

        Register a function to call before each test in a suite.

        Parameters
        fn: function()
        .
        code »describe ( name, fn )

        Registers a new test suite.

        Parameters
        name: string
        The suite name.
        fn: function()=
        The suite function, or undefined to define - a pending test suite.
        code »ignore ( predicateFn )!Object

        Ignores the test chained to this function if the provided predicate returns - true.

        Parameters
        predicateFn: function(): boolean
        A predicate to call to determine - if the test should be suppressed. This function MUST be synchronous.
        Returns
        An object with wrapped versions of #it() and - #describe() that ignore tests as indicated by the predicate.
        code »iit ( name, fn )

        An alias for #it() that flags the test as the only one that should - be run within the current suite.

        Parameters
        name: string
        The test name.
        fn: function()=
        The test function, or undefined to define - a pending test case.
        code »it ( name, fn )

        Add a test to the current suite.

        Parameters
        name: string
        The test name.
        fn: function()=
        The test function, or undefined to define - a pending test case.
        code »xdescribe ( name, fn )

        Defines a suppressed test suite.

        Parameters
        name: string
        The suite name.
        fn: function()=
        The suite function, or undefined to define - a pending test suite.
        code »xit ( name, fn )

        Adds a test to the current suite while suppressing it so it is not run.

        Parameters
        name: string
        The test name.
        fn: function()=
        The test function, or undefined to define - a pending test case.
        \ No newline at end of file +function maybe() { return Math.random() < 0.5; } + +

        Functions

        after(fn)code »

        Register a function to call after the current suite finishes.

        +
        Parameters
        fnfunction(): ?

        .

        +

        afterEach(fn)code »

        Register a function to call after each test in a suite.

        +
        Parameters
        fnfunction(): ?

        .

        +

        before(fn)code »

        Register a function to call before the current suite starts.

        +
        Parameters
        fnfunction(): ?

        .

        +

        beforeEach(fn)code »

        Register a function to call before each test in a suite.

        +
        Parameters
        fnfunction(): ?

        .

        +

        describe(name, fn)code »

        Registers a new test suite.

        +
        Parameters
        namestring

        The suite name.

        +
        fnfunction(): ?=

        The suite function, or undefined to define +a pending test suite.

        +

        ignore(predicateFn)code »

        Ignores the test chained to this function if the provided predicate returns +true.

        +
        Parameters
        predicateFnfunction(): boolean

        A predicate to call to determine +if the test should be suppressed. This function MUST be synchronous.

        +
        Returns
        Object

        An object with wrapped versions of #it() and +#describe() that ignore tests as indicated by the predicate.

        +

        iit(name, fn)code »

        An alias for #it() that flags the test as the only one that should +be run within the current suite.

        +
        Parameters
        namestring

        The test name.

        +
        fnfunction(): ?=

        The test function, or undefined to define +a pending test case.

        +

        it(name, fn)code »

        Add a test to the current suite.

        +
        Parameters
        namestring

        The test name.

        +
        fnfunction(): ?=

        The test function, or undefined to define +a pending test case.

        +

        xdescribe(name, fn)code »

        Defines a suppressed test suite.

        +
        Parameters
        namestring

        The suite name.

        +
        fnfunction(): ?=

        The suite function, or undefined to define +a pending test suite.

        +

        xit(name, fn)code »

        Adds a test to the current suite while suppressing it so it is not run.

        +
        Parameters
        namestring

        The test name.

        +
        fnfunction(): ?=

        The test function, or undefined to define +a pending test case.

        +
        \ No newline at end of file diff --git a/docs/module_selenium-webdriver_testing_assert.html b/docs/module_selenium-webdriver_testing_assert.html index d214b1f..d36cbdb 100644 --- a/docs/module_selenium-webdriver_testing_assert.html +++ b/docs/module_selenium-webdriver_testing_assert.html @@ -1,19 +1,9 @@ -selenium-webdriver/testing/assert

        Module selenium-webdriver/testing/assert

        code »

        Defines a library that simplifies writing assertions against - promised values. - -

        -
        - NOTE: This module is considered experimental and is subject to - change, or removal, at any time! -
        -
        - - Sample usage: -
        
        - var driver = new webdriver.Builder().build();
        - driver.get('http://www.google.com');
        -
        - assert(driver.getTitle()).equalTo('Google');
        - 

        Main

        assert ( value )!webdriver.testing.Assertion
        Parameters
        value: *
        The value to perform an assertion on.
        Returns
        The new assertion.
        Show:

        Functions

        code »register ( name, matcherTemplate )

        Registers a new assertion to expose from the - webdriver.testing.Assertion prototype.

        Parameters
        name: string
        The assertion name.
        matcherTemplate: (function(new: goog.labs.testing.Matcher, *)|{matches: function(*): boolean, describe: function(): string})
        Either the - matcher constructor to use, or an object literal defining a matcher.
        \ No newline at end of file +selenium-webdriver/testing/assert

        module selenium-webdriver/testing/assert

        Creates a new assertion.

        +

        assert(value)

        Parameters
        value*

        The value to perform an assertion on.

        +
        Returns
        webdriver.testing.Assertion

        The new assertion.

        +

        Functions

        register(name, matcherTemplate)code »

        Registers a new assertion to expose from the +webdriver.testing.Assertion prototype.

        +
        Parameters
        namestring

        The assertion name.

        +
        matcherTemplate(function(new: goog.labs.testing.Matcher, *): ?|{describe: function(): string, matches: function(*): boolean})

        Either the +matcher constructor to use, or an object literal defining a matcher.

        +
        \ No newline at end of file diff --git a/docs/namespace_PRIMITIVE_EQUALITY_PREDICATES.html b/docs/namespace_PRIMITIVE_EQUALITY_PREDICATES.html new file mode 100644 index 0000000..55bf9cd --- /dev/null +++ b/docs/namespace_PRIMITIVE_EQUALITY_PREDICATES.html @@ -0,0 +1 @@ +PRIMITIVE_EQUALITY_PREDICATES

        Namespace PRIMITIVE_EQUALITY_PREDICATES

        code »
        Show:

        Global Functions

        Parameters
        var1
        var2
        Parameters
        date1
        date2
        Parameters
        var1
        var2
        Parameters
        var1
        var2
        Parameters
        var1
        var2
        Parameters
        var1
        var2
        \ No newline at end of file diff --git a/docs/namespace_bot.html b/docs/namespace_bot.html index b3d27b1..994ded3 100644 --- a/docs/namespace_bot.html +++ b/docs/namespace_bot.html @@ -1,4 +1,4 @@ -bot

        Namespace bot

        code »

        Classes

        bot.Error
        Error extension that includes error status codes from the WebDriver wire - protocol: - http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes

        Enumerations

        bot.ErrorCode
        Error codes from the WebDriver wire protocol: - http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
        Show:
        \ No newline at end of file +bot

        namespace bot

        Types

        Error

        Represents an error returned from a WebDriver command request.

        +
        ErrorCode

        Error codes from the Selenium WebDriver protocol: +https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#response-status-codes

        +
        \ No newline at end of file diff --git a/docs/namespace_bot_json.html b/docs/namespace_bot_json.html index fa0a57e..54d65fa 100644 --- a/docs/namespace_bot_json.html +++ b/docs/namespace_bot_json.html @@ -1,4 +1,20 @@ -bot.json

        Namespace bot.json

        code »
        Show:

        Global Functions

        code »bot.json.parse ( jsonStr )*

        Parses a JSON string and returns the result.

        Parameters
        jsonStr: string
        The string to parse.
        Returns
        The JSON object.
        Throws
        Error
        If the input string is an invalid JSON string.
        code »bot.json.stringify ( jsonObj, opt_replacer )string

        Converts a JSON object to its string representation.

        Parameters
        jsonObj: *
        The input object.
        opt_replacer: ?(function(string, *): *)=
        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.
        Returns
        A JSON string representation of the input object.

        Global Properties

        Whether the current browser supports the native JSON interface.

        Compiler Constants

        \ No newline at end of file +bot.json

        namespace bot.json

        Functions

        parse(jsonStr)code »

        Parses a JSON string and returns the result.

        +
        Parameters
        jsonStrstring

        The string to parse.

        +
        Returns

        The JSON object.

        +
        Throws
        Error

        If the input string is an invalid JSON string.

        +

        stringify(jsonObj, opt_replacer)code »

        Converts a JSON object to its string representation.

        +
        Parameters
        jsonObj*

        The input object.

        +
        opt_replacer?function(string, *): *=

        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.

        +
        Returns
        string

        A JSON string representation of the input object.

        +

        Compiler Constants

        NATIVE_JSONboolean

        NATIVE_JSON indicates whether the code should rely on the +native JSON functions, if available.

        +

        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. +

        \ No newline at end of file diff --git a/docs/namespace_bot_response.html b/docs/namespace_bot_response.html index 3525f53..73e7453 100644 --- a/docs/namespace_bot_response.html +++ b/docs/namespace_bot_response.html @@ -1,5 +1,18 @@ -bot.response

        Namespace bot.response

        code »
        Show:

        Type Definitions

        code »bot.response.ResponseObject : {status: bot.ErrorCode, value: (*|{message: string})}
        Type definition for a response object, as defined by the JSON wire protocol.

        Global Functions

        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.

        Parameters
        responseObj: !bot.response.ResponseObject
        The response object to - check.
        Returns
        The checked response object.
        Throws
        bot.Error
        If the response describes an error.

        Converts an error value into its JSON representation as defined by the - WebDriver wire protocol.

        Parameters
        error: (bot.Error|Error|*)
        The error value to convert.
        Returns
        The new response object.

        Creates a new success response object with the provided value.

        Parameters
        value: *
        The response value.
        Returns
        The new response object.
        Parameters
        value: *
        The value to test.
        Returns
        Whether the given value is a response object.
        \ No newline at end of file +bot.response

        namespace bot.response

        Functions

        checkResponse(responseObj)code »

        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.

        +
        Parameters
        responseObj{status: number, value: *}

        The response object to +check.

        +
        Returns
        {status: number, value: *}

        The checked response object.

        +
        Throws
        bot.Error

        If the response describes an error.

        +

        createErrorResponse(error)code »

        Converts an error value into its JSON representation as defined by the +WebDriver wire protocol.

        +
        Parameters
        error*

        The error value to convert.

        +
        Returns
        {status: number, value: *}

        The new response object.

        +

        createResponse(value)code »

        Creates a new success response object with the provided value.

        +
        Parameters
        value*

        The response value.

        +
        Returns
        {status: number, value: *}

        The new response object.

        +

        isResponseObject(value)code »

        Parameters
        value*

        The value to test.

        +
        Returns
        boolean

        Whether the given value is a response object.

        +

        Type Definitions

        response.ResponseObject{status: number, value: *}

        Type definition for a response object, as defined by the JSON wire protocol.

        +
        \ No newline at end of file diff --git a/docs/namespace_bot_userAgent.html b/docs/namespace_bot_userAgent.html index cde2533..0a884fb 100644 --- a/docs/namespace_bot_userAgent.html +++ b/docs/namespace_bot_userAgent.html @@ -1,21 +1,33 @@ -bot.userAgent

        Namespace bot.userAgent

        code »
        Show:

        Global Functions

        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: -

          -
        1. in a Firefox extension, tests the engine version through the XUL version - comparator service, because no window.navigator object is available -
        2. in IE, compares the given version to the current documentMode -
        Parameters
        version: (string|number)
        The version number to check.
        Returns
        Whether the browser engine version is the same or higher - than the given 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: -

          -
        1. in a Firefox extension, tests the product version through the XUL version - comparator service, because no window.navigator object is available -
        2. on Android, always compares to the version to the OS version -
        Parameters
        version: (string|number)
        The version number to check.
        Returns
        Whether the browser product version is the same or higher - than the given version.

        Global Properties

        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.

        \ No newline at end of file +bot.userAgent

        namespace bot.userAgent

        Functions

        isEngineVersion(version)code »

        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:

        +
        1. in a Firefox extension, tests the engine version through the XUL version + comparator service, because no window.navigator object is available +
        2. in IE, compares the given version to the current documentMode +
        +
        Parameters
        version(string|number)

        The version number to check.

        +
        Returns
        boolean

        Whether the browser engine version is the same or higher +than the given version.

        +

        isProductVersion(version)code »

        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:

        +
        1. in a Firefox extension, tests the product version through the XUL version + comparator service, because no window.navigator object is available +
        2. on Android, always compares to the version to the OS version +
        +
        Parameters
        version(string|number)

        The version number to check.

        +
        Returns
        boolean

        Whether the browser product version is the same or higher +than the given version.

        +

        Properties

        ANDROID_PRE_GINGERBREADboolean

        Whether the current browser is Android pre-gingerbread.

        +
        ANDROID_PRE_ICECREAMSANDWICHboolean

        Whether the current browser is Android pre-icecreamsandwich

        +
        FIREFOX_EXTENSIONboolean

        Whether we are in a Firefox extension.

        +
        IE_DOC_10boolean

        Whether the current document is IE in IE10 (or newer) standards mode.

        +
        IE_DOC_9boolean

        Whether the current document is IE in IE9 (or newer) standards mode.

        +
        IE_DOC_PRE10boolean

        Whether the current document is IE in a documentMode older than 10.

        +
        IE_DOC_PRE8boolean

        Whether the current document is IE in a documentMode older than 8.

        +
        IE_DOC_PRE9boolean

        Whether the current document is IE in a documentMode older than 9.

        +
        IOSboolean

        Whether we are on IOS.

        +
        MOBILEboolean

        Whether we are on a mobile browser.

        +
        SAFARI_6boolean

        Whether the current browser is Safari 6.

        +
        WINDOWS_PHONEboolean

        Whether the current browser is Windows Phone.

        +
        \ No newline at end of file diff --git a/docs/namespace_goog.html b/docs/namespace_goog.html index 8c7b435..4d1beec 100644 --- a/docs/namespace_goog.html +++ b/docs/namespace_goog.html @@ -1,57 +1,53 @@ -goog

        Namespace goog

        code »

        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.

        Classes

        goog.Uri
        This class contains setters and getters for the parts of the URI.
        Show:

        Global Functions

        When defining a class Foo with an abstract method bar(), you can do: - +goog

        Namespace goog

        code »

        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.

        Classes

        goog.Disposable
        Class that provides the basic implementation for disposable objects.
        goog.Uri
        This class contains setters and getters for the parts of the URI.
        Show:

        Global Functions

        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. + 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.

        Throws
        Error
        when invoked to indicate the method should be - overridden.
        code »goog.addDependency ( relPath, provides, requires )

        Adds a dependency from a file to the files it requires.

        Parameters
        relPath: string
        The path to the js file.
        provides: Array
        An array of strings with the names of the objects + 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.
        Throws
        Error
        when invoked to indicate the method should be overridden.
        code »goog.addDependency ( relPath, provides, requires, opt_isModule )

        Adds a dependency from a file to the files it requires.

        Parameters
        relPath: string
        The path to the js file.
        provides: Array
        An array of strings with the names of the objects this file provides.
        requires: Array
        An array of strings with the names of the objects - this file requires.

        Adds a getInstance static method that always return the same instance - object.

        Parameters
        ctor: !Function
        The constructor for the class to add the static - method to.
        code »goog.base ( me, opt_methodName, var_args )*

        Call up to the superclass. + this file requires.

        opt_isModule: boolean=
        Whether this dependency must be loaded as + a module as declared by goog.module.

        Adds a getInstance static method that always returns the same + instance object.

        Parameters
        ctor: !Function
        The constructor for the class to add the static + method to.
        code »goog.base ( me, opt_methodName, var_args )*

        Call up to the superclass. If this is called from a constructor, then this calls the superclass - contructor with arguments 1-N. + 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. + 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 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.

        Parameters
        me: !Object
        Should always be "this".
        opt_methodName: *=
        The method name if calling a super method.
        var_args: ...*
        The rest of the arguments.
        Returns
        The return value of the superclass method.
        code »<T> goog.bind ( fn, selfObj, var_args )!Function

        Partially applies this function to a particular 'this object' and zero or + 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.

        Parameters
        me: !Object
        Should always be "this".
        opt_methodName: *=
        The method name if calling a super method.
        var_args: ...*
        The rest of the arguments.
        Returns
        The return value of the superclass method.
        code »<T> goog.bind ( fn, selfObj, var_args )!Function

        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'.

        + function pre-filled and the value of this 'pre-specified'. - Remaining arguments specified at call-time are appended to the pre- - specified ones.

        + Remaining arguments specified at call-time are appended to the pre-specified + ones. - Also see: #partial.

        + Also see: #partial. Usage:

        var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2');
        - barMethBound('arg3', 'arg4');
        Parameters
        fn: ?function(this: T, ...)
        A function to partially apply.
        selfObj: T
        Specifies the object which |this| should - point to when the function is run.
        var_args: ...*
        Additional arguments that are partially - applied to the function.
        Returns
        A partially-applied form of the function bind() was - invoked as a method of.
        code »goog.bindJs_ ( fn, selfObj, var_args )!Function

        A pure-JS implementation of goog.bind.

        Parameters
        fn: Function
        A function to partially apply.
        selfObj: (Object|undefined)
        Specifies the object which |this| should - point to when the function is run.
        var_args: ...*
        Additional arguments that are partially - applied to the function.
        Returns
        A partially-applied form of the function bind() was - invoked as a method of.
        code »goog.bindNative_ ( fn, selfObj, var_args )!Function

        A native implementation of goog.bind.

        Parameters
        fn: Function
        A function to partially apply.
        selfObj: (Object|undefined)
        Specifies the object which |this| should - point to when the function is run.
        var_args: ...*
        Additional arguments that are partially - applied to the function.
        Returns
        A partially-applied form of the function bind() was - invoked as a method of.
        Deprecated: goog.cloneObject is unsafe. Prefer the goog.object methods.

        Clones a value. The input may be an Object, Array, or basic type. Objects and + barMethBound('arg3', 'arg4');

        Parameters
        fn: ?function(this: T, ...)
        A function to partially apply.
        selfObj: T
        Specifies the object which this should point to when the + function is run.
        var_args: ...*
        Additional arguments that are partially applied to the + function.
        Returns
        A partially-applied form of the function bind() was + invoked as a method of.
        code »goog.bindJs_ ( fn, selfObj, var_args )!Function

        A pure-JS implementation of goog.bind.

        Parameters
        fn: Function
        A function to partially apply.
        selfObj: (Object|undefined)
        Specifies the object which this should + point to when the function is run.
        var_args: ...*
        Additional arguments that are partially applied to the + function.
        Returns
        A partially-applied form of the function bind() was + invoked as a method of.
        code »goog.bindNative_ ( fn, selfObj, var_args )!Function

        A native implementation of goog.bind.

        Parameters
        fn: Function
        A function to partially apply.
        selfObj: (Object|undefined)
        Specifies the object which this should + point to when the function is run.
        var_args: ...*
        Additional arguments that are partially applied to the + function.
        Returns
        A partially-applied form of the function bind() was + invoked as a method of.
        Deprecated: goog.cloneObject is unsafe. Prefer the goog.object methods.

        Clones a value. The input may be an Object, Array, or basic type. Objects and arrays will be cloned recursively. WARNINGS: @@ -59,60 +55,83 @@ refer to themselves will cause infinite recursion. goog.cloneObject is unaware of unique identifiers, and copies - UIDs created by getUid into cloned results.

        Parameters
        obj: *
        The value to clone.
        Returns
        A clone of the input value.
        code »goog.define ( name, defaultValue )

        Defines a named value. In uncompiled mode, the value is retreived from - CLOSURE_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.

        Parameters
        name: string
        The distinguished name to provide.
        defaultValue
        code »goog.exportPath_ ( name, opt_object, opt_objectToExportTo )

        Builds an object structure for the provided namespace path, - ensuring that names that already exist are not overwritten. For - example: + UIDs created by getUid into cloned results.

        Parameters
        obj: *
        The value to clone.
        Returns
        A clone of the input value.
        code »goog.define ( name, defaultValue )

        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.

        Parameters
        name: string
        The distinguished name to provide.
        defaultValue
        code »goog.defineClass ( superClass, def )!Function

        Creates a restricted form of a Closure "class": + - from the compiler's perspective, the instance returned from the + constructor is sealed (no new properties may be added). This enables + better checks. + - the compiler will rewrite this definition to a form that is optimal + for type checking and optimization (initially this will be a more + traditional form).

        Parameters
        superClass: Function
        The superclass, Object or null.
        def: goog.defineClass.ClassDescriptor
        An object literal describing the + the class. It may have the following properties: + "constructor": the constructor function + "statics": an object literal containing methods to add to the constructor + as "static" methods or a function that will receive the constructor + function as its only parameter to which static properties can + be added. + all other properties are added to the prototype.
        Returns
        The class constructor.

        Calls dispose on the argument if it supports it. If obj is not an + object with a dispose() method, this is a no-op.

        Parameters
        obj: *
        The object to dispose of.

        Calls dispose on each member of the list that supports it. (If the + member is an ArrayLike, then goog.disposeAll() will be called + recursively on each of its members.) If the member is not an object with a + dispose() method, then it is ignored.

        Parameters
        var_args: ...*
        The list.
        code »goog.exportPath_ ( name, opt_object, opt_objectToExportTo )

        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.

        Parameters
        name: string
        name of the object that this file defines.
        opt_object: *=
        the object to expose at the end of the path.
        opt_objectToExportTo: Object=
        The object to add the path to; default - is |goog.global|.
        code »goog.exportProperty ( object, publicName, symbol )

        Exports a property unobfuscated into the object's namespace. + is |goog.global|.

        code »goog.exportProperty ( object, publicName, symbol )

        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);

        Parameters
        object: Object
        Object whose static property is being exported.
        publicName: string
        Unobfuscated name to export.
        symbol: *
        Object the name should point to.
        code »goog.exportSymbol ( publicPath, object, opt_objectToExportTo )

        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 + ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);

        Parameters
        object: Object
        Object whose static property is being exported.
        publicName: string
        Unobfuscated name to export.
        symbol: *
        Object the name should point to.
        code »goog.exportSymbol ( publicPath, object, opt_objectToExportTo )

        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. + 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); + 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();

        Parameters
        publicPath: string
        Unobfuscated name to export.
        object: *
        Object the name should point to.
        opt_objectToExportTo: Object=
        The object to add the path to; default - is |goog.global|.

        Tries to detect the base path of the base.js script that bootstraps Closure

        code »goog.getCssName ( className, opt_modifier )string

        Handles strings that are intended to be used as CSS class names. + is goog.global.

        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.

        Parameters
        name: string
        The namespace to forward declare in the form of + "goog.package.part".
        code »goog.getCssName ( className, opt_modifier )string

        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. + 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 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. + 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.

        Parameters
        className: string
        The class name.
        opt_modifier: string=
        A modifier to be appended to the class name.
        Returns
        The class name or the concatenation of the class name and - the modifier.
        Deprecated: Use goog.getUid instead.

        Adds a hash code field to an object. The hash code is unique for the - given object.

        Parameters
        obj: Object
        The object to get the hash code for.
        Returns
        The hash code for the object.
        code »goog.getMsg ( str, opt_values )string

        Gets a localized message. + 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.

        Parameters
        className: string
        The class name.
        opt_modifier: string=
        A modifier to be appended to the class name.
        Returns
        The class name or the concatenation of the class name and + the modifier.
        Deprecated: Use goog.getUid instead.

        Adds a hash code field to an object. The hash code is unique for the + given object.

        Parameters
        obj: Object
        The object to get the hash code for.
        Returns
        The hash code for the object.
        code »goog.getMsg ( str, opt_values )string

        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 @@ -121,87 +140,95 @@ Messages must be initialized in the form: var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'}); -

        Parameters
        str: string
        Translatable string, places holders in the form {$foo}.
        opt_values: Object=
        Map of place holder name to value.
        Returns
        message with placeholders filled.

        Gets a localized message. If the message does not have a translation, gives a +

        Parameters
        str: string
        Translatable string, places holders in the form {$foo}.
        opt_values: Object=
        Map of place holder name to value.
        Returns
        message with placeholders filled.

        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 primtive. Must be used in the form: + This function is a compiler primitive. Must be used in the form: var x = goog.getMsgWithFallback(MSG_A, MSG_B); - where MSG_A and MSG_B were initialized with goog.getMsg.

        Parameters
        a: string
        The preferred message.
        b: string
        The fallback message.
        Returns
        The best translated message.
        code »goog.getObjectByName ( name, opt_obj )

        Returns an object based on its fully qualified external name. If you are - using a compilation pass that renames property names beware that using this - function will not find renamed properties.

        Parameters
        name: string
        The fully qualified name.
        opt_obj: Object=
        The object within which to look; default is - |goog.global|.
        Returns
        The value (object or primitive) or, if not found, null.

        Looks at the dependency rules and tries to determine the script file that - fulfills a particular rule.

        Parameters
        rule: string
        In the form goog.namespace.Class or project.script.
        Returns
        Url corresponding to the rule, or null.

        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 getUid. There is no guarantee that the ID is unique - or consistent across sessions. It is unsafe to generate unique ID for - function prototypes.

        Parameters
        obj: Object
        The object to get the unique ID for.
        Returns
        The unique ID for the object.

        Evals javascript in the global scope. In IE this uses execScript, other + where MSG_A and MSG_B were initialized with goog.getMsg.

        Parameters
        a: string
        The preferred message.
        b: string
        The fallback message.
        Returns
        The best translated message.
        code »goog.getObjectByName ( name, opt_obj )

        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.

        Parameters
        name: string
        The fully qualified name.
        opt_obj: Object=
        The object within which to look; default is + |goog.global|.
        Returns
        The value (object or primitive) or, if not found, null.

        Looks at the dependency rules and tries to determine the script file that + fulfills a particular rule.

        Parameters
        rule: string
        In the form goog.namespace.Class or project.script.
        Returns
        Url corresponding to the rule, or null.

        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 getUid. There is no guarantee that the ID is unique or + consistent across sessions. It is unsafe to generate unique ID for function + prototypes.

        Parameters
        obj: Object
        The object to get the unique ID for.
        Returns
        The unique ID for the object.

        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.

        Parameters
        script: string
        JavaScript string.
        code »goog.globalize ( obj, opt_global )
        Deprecated: Properties may be explicitly exported to the global scope, but - this should no longer be done in bulk.

        Globalizes a whole namespace, such as goog or goog.lang.

        Parameters
        obj: Object
        The namespace to globalize.
        opt_global: Object=
        The object to add the properties to.
        code »goog.identityFunction ( opt_returnValue, var_args )
        Deprecated: Use goog.functions.identity instead.

        The identity function. Returns its first argument.

        Parameters
        opt_returnValue: *=
        The single value that will be returned.
        var_args: ...*
        Optional trailing arguments. These are ignored.
        Returns
        The first argument. We can't know the type -- just pass it along - without type.

        Imports a script if, and only if, that script hasn't already been imported. - (Must be called at execution time)

        Parameters
        src: string
        Script source.

        Tries to detect whether is in the context of an HTML document.

        Returns
        True if it looks like HTML document.
        code »goog.inherits ( childCtor, parentCtor )

        Inherit the prototype methods from one constructor into another. + Throws an exception if neither execScript or eval is defined.

        Parameters
        script: string
        JavaScript string.
        code »goog.globalize ( obj, opt_global )
        Deprecated: Properties may be explicitly exported to the global scope, but + this should no longer be done in bulk.

        Globalizes a whole namespace, such as goog or goog.lang.

        Parameters
        obj: Object
        The namespace to globalize.
        opt_global: Object=
        The object to add the properties to.

        Whether the given object is alreay assigned a unique ID. + + This does not modify the object.

        Parameters
        obj: Object
        The object to check.
        Returns
        Whether there an assigned unique id for the object.
        code »goog.identityFunction ( opt_returnValue, var_args )
        Deprecated: Use goog.functions.identity instead.

        The identity function. Returns its first argument.

        Parameters
        opt_returnValue: *=
        The single value that will be returned.
        var_args: ...*
        Optional trailing arguments. These are ignored.
        Returns
        The first argument. We can't know the type -- just pass it along + without type.

        Given a URL initiate retrieval and execution of the module.

        Parameters
        src: string
        Script source URL.
        code »goog.importScript_ ( src, opt_sourceText )

        Imports a script if, and only if, that script hasn't already been imported. + (Must be called at execution time)

        Parameters
        src: string
        Script source.
        opt_sourceText: string=
        The optionally source text to evaluate

        Tries to detect whether is in the context of an HTML document.

        Returns
        True if it looks like HTML document.
        code »goog.inherits ( childCtor, parentCtor )

        Inherit the prototype methods from one constructor into another. Usage:

          function ParentClass(a, b) { }
        - ParentClass.prototype.foo = function(a) { }
        + ParentClass.prototype.foo = function(a) { };
         
          function ChildClass(a, b, c) {
        -   goog.base(this, a, b);
        +   ChildClass.base(this, 'constructor', a, b);
          }
          goog.inherits(ChildClass, ParentClass);
         
          var child = new ChildClass('a', 'b', 'see');
        - child.foo(); // 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
        - };
        - 
        Parameters
        childCtor: Function
        Child class.
        parentCtor: Function
        Parent class.

        Returns true if the specified value is an array

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is an array.

        Returns true if the object looks like an array. To qualify as array like + child.foo(); // This works. +

        Parameters
        childCtor: Function
        Child class.
        parentCtor: Function
        Parent class.

        Returns true if the specified value is an array.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable 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.

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is an array.

        Returns true if the specified value is a boolean

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is 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.

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is a like a Date.

        Returns true if the specified value is not |undefined|. + property.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is an array.

        Returns true if the specified value is a boolean.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is 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.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is a like a Date.

        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. Additionally, this function assumes that the global - undefined variable has not been redefined.

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is defined.

        Returns true if the specified value is defined and not null

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is defined and not null.

        Returns true if the specified value is a function

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is a function.

        Returns true if the specified value is |null|

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is null.

        Returns true if the specified value is a number

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is a number.

        Returns true if the specified value is an object. This includes arrays - and functions.

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is an object.

        Check if the given name has been goog.provided. This will return false for - names that are available only as implicit namespaces.

        Parameters
        name: string
        name of the object to look for.
        Returns
        Whether the name has been provided.

        Returns true if the specified value is a string

        Parameters
        val: *
        Variable to test.
        Returns
        Whether variable is a string.
        code »goog.mixin ( target, source )

        Copies all the members of a source object to a target object. This method + operator instead.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is defined.

        Returns true if the specified value is defined and not null.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is defined and not null.

        Returns true if the specified value is a function.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is a function.
        Returns
        Whether a goog.module is currently being initialized.

        Returns true if the specified value is null.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is null.

        Returns true if the specified value is a number.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is a number.

        Returns true if the specified value is an object. This includes arrays and + functions.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is an object.

        Check if the given name has been goog.provided. This will return false for + names that are available only as implicit namespaces.

        Parameters
        name: string
        name of the object to look for.
        Returns
        Whether the name has been provided.

        Returns true if the specified value is a string.

        Parameters
        val: ?
        Variable to test.
        Returns
        Whether variable is a string.
        Parameters
        moduleDef: (function(?): ?|string)
        The module definition.

        Load any deferred goog.module loads.

        Parameters
        msg
        code »goog.mixin ( target, source )

        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.

        Parameters
        target: Object
        Target.
        source: Object
        Source.
        Returns
        An integer value representing the number of milliseconds - between midnight, January 1, 1970 and the current time.

        Null function used for default values of callbacks, etc.

        Returns
        Nothing.
        code »goog.partial ( fn, var_args )!Function

        Like bind(), except that a 'this object' is not required. Useful when the + toString or hasOwnProperty. Use goog.object.extend for this purpose.

        Parameters
        target: Object
        Target.
        source: Object
        Source.

        goog.module serves two purposes: + - marks a file that must be loaded as a module + - reserves a namespace (it can not also be goog.provided) + and has three requirements: + - goog.module may not be used in the same file as goog.provide. + - goog.module must be the first statement in the file. + - only one goog.module is allowed per file. + When a goog.module annotated file is loaded, it is loaded enclosed in + a strict function closure. This means that: + - any variable declared in a goog.module file are private to the file, + not global. Although the compiler is expected to inline the module. + - The code must obey all the rules of "strict" JavaScript. + - the file will be marked as "use strict" + + NOTE: unlike goog.provide, goog.module does not declare any symbols by + itself.

        Parameters
        name: string
        Namespace provided by this file in the form + "goog.package.part", is expected but not required.
        Returns
        An integer value representing the number of milliseconds + between midnight, January 1, 1970 and the current time.

        Null function used for default values of callbacks, etc.

        Returns
        Nothing.
        code »goog.onScriptLoad_ ( script, scriptIndex )boolean

        A readystatechange handler for legacy IE

        Parameters
        script
        scriptIndex
        code »goog.partial ( fn, var_args )!Function

        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);

        Parameters
        fn: Function
        A function to partially apply.
        var_args: ...*
        Additional arguments that are partially - applied to fn.
        Returns
        A partially-applied form of the function bind() was - invoked as a method of.

        Creates object stubs for a namespace. The presence of one or more + g(arg3, arg4);

        Parameters
        fn: Function
        A function to partially apply.
        var_args: ...*
        Additional arguments that are partially applied to fn.
        Returns
        A partially-applied form of the function bind() was + invoked as a method of.

        Creates object stubs for a namespace. The presence of one or more goog.provide() calls indicate that the file defines the given - objects/namespaces. Build tools also scan for provide/require statements + 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.

        Parameters
        name: string
        Namespace provided by this file in the form - "goog.package.part".
        Deprecated: Use goog.removeUid instead.

        Removes the hash code field from an object.

        Parameters
        obj: Object
        The object to remove the field from.

        Removes the unique ID from an object. This is useful if the object was + "goog.package.part".

        Deprecated: Use goog.removeUid instead.

        Removes the hash code field from an object.

        Parameters
        obj: Object
        The object to remove the field from.

        Removes the unique ID from an object. This is useful if the object was previously mutated using goog.getUid in which case the mutation is - undone.

        Parameters
        obj: Object
        The object to remove the unique ID field from.

        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.

        Parameters
        name: string
        Namespace to include (as was given in goog.provide()) - in the form "goog.package.part".

        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.

        Parameters
        fn: function()
        Function to call. This function can contain aliases + undone.
        Parameters
        obj: Object
        The object to remove the unique ID field from.

        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.

        Parameters
        name: string
        Namespace to include (as was given in goog.provide()) in + the form "goog.package.part".
        Returns
        If called within a goog.module file, the associated namespace or + module otherwise null.

        Retrieve and execute a module.

        Parameters
        src: string
        Script source URL.

        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.

        Parameters
        fn: function()
        Function to call. This function can contain aliases to namespaces (e.g. "var dom = goog.dom") or classes - (e.g. "var Timer = goog.Timer").
        code »goog.setCssNameMapping ( mapping, opt_style )

        Sets the map to check when returning a value from goog.getCssName(). Example: + (e.g. "var Timer = goog.Timer").

        code »goog.setCssNameMapping ( mapping, opt_style )

        Sets the map to check when returning a value from goog.getCssName(). Example:

          goog.setCssNameMapping({
            "goog": "a",
        @@ -217,24 +244,30 @@
          --closure_pass flag is set.
        Parameters
        mapping: !Object
        A map of strings to strings where keys are possible arguments to goog.getCssName() and values are the corresponding values that should be returned.
        opt_style: string=
        The style of css name mapping. There are two valid - options: 'BY_PART', and 'BY_WHOLE'.
        code »goog.setTestOnly ( opt_message )

        Marks that the current file should only be used for testing, and never for + options: 'BY_PART', and 'BY_WHOLE'.

        code »goog.setTestOnly ( opt_message )

        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).

        Parameters
        opt_message: string=
        Optional message to add to the error that's - raised when used in production 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'.

        Parameters
        value: *
        The value to get the type of.
        Returns
        The name of the type.

        The default implementation of the import function. Writes a script tag to - import the script.

        Parameters
        src: string
        The script source.
        Returns
        True if the script was imported, false otherwise.

        Resolves dependencies based on the dependencies added using addDependency - and calls importScript_ in the correct order.

        Global Properties

        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 + 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).

        Parameters
        opt_message: string=
        Optional message to add to the error that's + raised when used in production code.

        Sealing classes breaks the older idiom of assigning properties on the + prototype rather than in the constructor. As such, goog.defineClass + must not seal subclasses of these old-style classes until they are fixed. + Until then, this marks a class as "broken", instructing defineClass + not to seal subclasses.

        Parameters
        ctr: !Function
        The legacy constructor to tag as unsealable.

        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'.

        Parameters
        value: *
        The value to get the type of.
        Returns
        The name of the type.
        code »goog.wrapModule_ ( srcUrl, scriptText )string

        Return an appropriate module text. Suitable to insert into + a script tag (that is unescaped).

        Parameters
        srcUrl
        scriptText
        code »goog.writeScriptTag_ ( src, opt_sourceText )boolean

        The default implementation of the import function. Writes a script tag to + import the script.

        Parameters
        src: string
        The script url.
        opt_sourceText: string=
        The optionally source text to evaluate
        Returns
        True if the script was imported, false otherwise.

        Resolves dependencies based on the dependencies added using addDependency + and calls importScript_ in the correct order.

        Global Properties

        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.

        Name for unsealable tag property.

        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

        code »goog.global : global this

        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 + empirically tests whether eval works for globals). @see goog.globalEval

        code »goog.global : global this

        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 goog.testing.singleton module. The compiler - removes this variable if unused.

        Compiler Constants

        \ No newline at end of file + removes this variable if unused.

        The registry of initialized modules: + the module identifier to module exports map.

        code »goog.moduleLoaderState_ : ({moduleName: (string|undefined), exportTestMethods: boolean}|null)

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_array.html b/docs/namespace_goog_array.html index 1c8f008..ae5a09b 100644 --- a/docs/namespace_goog_array.html +++ b/docs/namespace_goog_array.html @@ -1,12 +1,13 @@ -goog.array

        Namespace goog.array

        code »
        Show:

        Type Definitions

        Global Functions

        code »<T> goog.array.binaryInsert ( array, value, opt_compareFn )boolean

        Inserts a value into a sorted array. The array is not modified if the - value is already present.

        Parameters
        array: Array.<T>
        The array to modify.
        value: T
        The object to insert.
        opt_compareFn: ?function(T, T): number=
        Optional comparison function by - which the - array is ordered. Should take 2 arguments to compare, and return a - negative number, zero, or a positive number depending on whether the - first argument is less than, equal to, or greater than the second.
        Returns
        True if an element was inserted.
        code »goog.array.binaryRemove ( array, value, opt_compareFn )boolean

        Removes a value from a sorted array.

        Parameters
        array: Array
        The array to modify.
        value: *
        The object to remove.
        opt_compareFn: Function=
        Optional comparison function by which the - array is ordered. Should take 2 arguments to compare, and return a - negative number, zero, or a positive number depending on whether the - first argument is less than, equal to, or greater than the second.
        Returns
        True if an element was removed.
        code »goog.array.binarySearch ( arr, target, opt_compareFn )number

        Searches the specified array for the specified target using the binary +goog.array

        Namespace goog.array

        code »
        Show:

        Type Definitions

        Global Functions

        code »<VALUE> goog.array.binaryInsert ( array, value, opt_compareFn )boolean

        Inserts a value into a sorted array. The array is not modified if the + value is already present.

        Parameters
        array: (Array.<VALUE>|goog.array.ArrayLike)
        The array to modify.
        value: VALUE
        The object to insert.
        opt_compareFn: function(VALUE, VALUE): number=
        Optional comparison + function by which the array is ordered. Should take 2 arguments to + compare, and return a negative number, zero, or a positive number + depending on whether the first argument is less than, equal to, or + greater than the second.
        Returns
        True if an element was inserted.
        code »<VALUE> goog.array.binaryRemove ( array, value, opt_compareFn )boolean

        Removes a value from a sorted array.

        Parameters
        array: (!Array.<VALUE>|!goog.array.ArrayLike)
        The array to modify.
        value: VALUE
        The object to remove.
        opt_compareFn: function(VALUE, VALUE): number=
        Optional comparison + function by which the array is ordered. Should take 2 arguments to + compare, and return a negative number, zero, or a positive number + depending on whether the first argument is less than, equal to, or + greater than the second.
        Returns
        True if an element was removed.
        code »<TARGET, VALUE> goog.array.binarySearch ( arr, target, opt_compareFn )number

        Searches the specified array for the specified target using the binary search algorithm. If no opt_compareFn is specified, elements are compared using goog.array.defaultCompare, which compares the elements using the built in < and > operators. This will produce the expected @@ -16,13 +17,14 @@ If the array contains multiple instances of the specified target value, any of these instances may be found. - Runtime: O(log n)

        Parameters
        arr: goog.array.ArrayLike
        The array to be searched.
        target: *
        The sought value.
        opt_compareFn: Function=
        Optional comparison function by which the - array is ordered. Should take 2 arguments to compare, and return a - negative number, zero, or a positive number depending on whether the - first argument is less than, equal to, or greater than the second.
        Returns
        Lowest index of the target value if found, otherwise + Runtime: O(log n)
        Parameters
        arr: (Array.<VALUE>|goog.array.ArrayLike)
        The array to be searched.
        target: TARGET
        The sought value.
        opt_compareFn: function(TARGET, VALUE): number=
        Optional comparison + function by which the array is ordered. Should take 2 arguments to + compare, and return a negative number, zero, or a positive number + depending on whether the first argument is less than, equal to, or + greater than the second.
        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.
        code »goog.array.binarySearch_ ( arr, compareFn, isEvaluator, opt_target, opt_selfObj )number

        Implementation of a binary search algorithm which knows how to use both + iff target is found.

        code »<THIS, VALUE, TARGET> goog.array.binarySearch_ ( arr, compareFn, isEvaluator, opt_target, opt_selfObj )number

        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 @@ -31,40 +33,44 @@ This implementation purposefully does not use goog.bind or goog.partial for performance reasons. - Runtime: O(log n)

        Parameters
        arr: goog.array.ArrayLike
        The array to be searched.
        compareFn: Function
        Either an evaluator or a comparison function, - as defined by binarySearch and binarySelect above.
        isEvaluator: boolean
        Whether the function is an evaluator or a - comparison function.
        opt_target: *=
        If the function is a comparison function, then this is - the target to binary search for.
        opt_selfObj: Object=
        If the function is an evaluator, this is an + Runtime: O(log n)
        Parameters
        arr: (Array.<VALUE>|goog.array.ArrayLike)
        The array to be searched.
        compareFn: (function(TARGET, VALUE): number|function(this: THIS, VALUE, number, ?): number)
        Either an + evaluator or a comparison function, as defined by binarySearch + and binarySelect above.
        isEvaluator: boolean
        Whether the function is an evaluator or a + comparison function.
        opt_target: TARGET=
        If the function is a comparison function, then + this is the target to binary search for.
        opt_selfObj: THIS=
        If the function is an evaluator, this is an optional this object for the evaluator.
        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.
        code »goog.array.binarySelect ( arr, evaluator, opt_obj )number

        Selects an index in the specified array using the binary search algorithm. + iff target is found.

        code »<THIS, VALUE> goog.array.binarySelect ( arr, evaluator, opt_obj )number

        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)

        Parameters
        arr: goog.array.ArrayLike
        The array to be searched.
        evaluator: Function
        Evaluator function that receives 3 arguments - (the element, the index and the array). Should return a negative number, - zero, or a positive number depending on whether the desired index is - before, at, or after the element passed to it.
        opt_obj: Object=
        The object to be used as the value of 'this' + Runtime: O(log n)
        Parameters
        arr: (Array.<VALUE>|goog.array.ArrayLike)
        The array to be searched.
        evaluator: function(this: THIS, VALUE, number, ?): number
        Evaluator function that receives 3 arguments (the element, the index and + the array). Should return a negative number, zero, or a positive number + depending on whether the desired index is before, at, or after the + element passed to it.
        opt_obj: THIS=
        The object to be used as the value of 'this' within evaluator.
        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.
        code »<T, S> goog.array.bucket ( array, sorter, opt_obj )!Object

        Splits an array into disjoint buckets according to a splitting function.

        Parameters
        array: Array.<T>
        The array.
        sorter: function(this: S, T, number, Array.<T>): ?
        Function to call for + iff a match is found.
        code »<T, S> goog.array.bucket ( array, sorter, opt_obj )!Object

        Splits an array into disjoint buckets according to a splitting function.

        Parameters
        array: Array.<T>
        The array.
        sorter: function(this: S, T, number, Array.<T>): ?
        Function to call for every element. This takes 3 arguments (the element, the index and the array) and must return a valid object key (a string, number, etc), or undefined, if that object should not be placed in a bucket.
        opt_obj: S=
        The object to be used as the value of 'this' within sorter.
        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.

        Clears the array.

        Parameters
        arr: goog.array.ArrayLike
        Array or array like object to clear.

        Does a shallow copy of an array.

        Parameters
        arr: goog.array.ArrayLike
        Array or array-like object to clone.
        Returns
        Clone of the input array.
        code »goog.array.compare ( arr1, arr2, opt_equalsFn )boolean
        Deprecated: Use goog.array.equals.
        code »goog.array.compare3 ( arr1, arr2, opt_compareFn )number

        3-way array compare function.

        Parameters
        arr1: !goog.array.ArrayLike
        The first array to compare.
        arr2: !goog.array.ArrayLike
        The second array to compare.
        opt_compareFn: ?function(?, ?): number=
        Optional comparison function - by which the array is to be ordered. Should take 2 arguments to compare, - and return a negative number, zero, or a positive number depending on - whether the first argument is less than, equal to, or greater than the - second.
        Returns
        Negative number, zero, or a positive number depending on + which the splitter returned that key.

        Clears the array.

        Parameters
        arr: goog.array.ArrayLike
        Array or array like object to clear.
        code »<T> goog.array.clone ( arr )!Array.<T>

        Does a shallow copy of an array.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array-like object to + clone.
        Returns
        Clone of the input array.
        code »<VALUE> goog.array.compare3 ( arr1, arr2, opt_compareFn )number

        3-way array compare function.

        Parameters
        arr1: (!Array.<VALUE>|!goog.array.ArrayLike)
        The first array to + compare.
        arr2: (!Array.<VALUE>|!goog.array.ArrayLike)
        The second array to + compare.
        opt_compareFn: function(VALUE, VALUE): number=
        Optional comparison + function by which the array is to be ordered. Should take 2 arguments to + compare, and return a negative number, zero, or a positive number + depending on whether the first argument is less than, equal to, or + greater than the second.
        Returns
        Negative number, zero, or a positive number depending on whether the first argument is less than, equal to, or greater than the - second.

        Returns a new array that is the result of joining the arguments. If arrays + second.

        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. @@ -85,18 +91,18 @@ Internally goog.array should use this, so that all methods will continue to work on these broken array objects.

        Parameters
        var_args: ...*
        Items to concatenate. Arrays will have each item - added, while primitives and objects will be added as is.
        Returns
        The new resultant array.

        Whether the array contains the given object.

        Parameters
        arr: goog.array.ArrayLike
        The array to test for the presence of the - element.
        obj: *
        The object for which to test.
        Returns
        true if obj is present.
        code »<T, S> goog.array.count ( arr, f, opt_obj )number

        Counts the array elements that fulfill the predicate, i.e. for which the + added, while primitives and objects will be added as is.Returns

        The new resultant array.

        Whether the array contains the given object.

        Parameters
        arr: goog.array.ArrayLike
        The array to test for the presence of the + element.
        obj: *
        The object for which to test.
        Returns
        true if obj is present.
        code »<T, S> goog.array.count ( arr, f, opt_obj )number

        Counts the array elements that fulfill the predicate, i.e. for which the callback function returns true. Skips holes in the array.

        Parameters
        arr: !(Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: function(this: S, T, number, ?): boolean
        The function to call for - every element. Takes 3 arguments (the element, the index and the array).
        opt_obj: S=
        The object to be used as the value of 'this' within f.
        Returns
        The number of the matching elements.

        Compares its two arguments for order, using the built in < and > - operators.

        Parameters
        a: *
        The first object to be compared.
        b: *
        The second object to be compared.
        Returns
        A negative number, zero, or a positive number as the first - argument is less than, equal to, or greater than the second.

        Compares its two arguments for equality, using the built in === operator.

        Parameters
        a: *
        The first object to compare.
        b: *
        The second object to compare.
        Returns
        True if the two arguments are equal, false otherwise.
        code »goog.array.equals ( arr1, arr2, opt_equalsFn )boolean

        Compares two arrays for equality. Two arrays are considered equal if they + every element. Takes 3 arguments (the element, the index and the array).

        opt_obj: S=
        The object to be used as the value of 'this' within f.Returns
        The number of the matching elements.

        Compares its two arguments for order, using the built in < and > + operators.

        Parameters
        a: VALUE
        The first object to be compared.
        b: VALUE
        The second object to be compared.
        Returns
        A negative number, zero, or a positive number as the first + argument is less than, equal to, or greater than the second.

        Compares its two arguments for equality, using the built in === operator.

        Parameters
        a: *
        The first object to compare.
        b: *
        The second object to compare.
        Returns
        True if the two arguments are equal, false otherwise.
        code »goog.array.equals ( arr1, arr2, opt_equalsFn )boolean

        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.

        Parameters
        arr1: goog.array.ArrayLike
        The first array to compare.
        arr2: goog.array.ArrayLike
        The second array to compare.
        opt_equalsFn: Function=
        Optional comparison function. Should take 2 arguments to compare, and return true if the arguments are equal. Defaults to goog.array.defaultCompareEquality which - compares the elements using the built-in '===' operator.
        Returns
        Whether the two arrays are equal.
        code »<T, S> goog.array.every ( arr, f, opt_obj )boolean

        Call f for each element of an array. If all calls return true, every() + compares the elements using the built-in '===' operator.Returns

        Whether the two arrays are equal.
        code »<T, S> goog.array.every ( arr, f, opt_obj )boolean

        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. @@ -104,7 +110,7 @@ like object over which to iterate.

        f: ?function(this: S, T, number, ?): boolean
        The function to call for for every element. This function takes 3 arguments (the element, the index and the array) and should return a boolean.
        opt_obj: S=
        The object to be used as the value of 'this' - within f.Returns
        false if any element fails the test.
        code »goog.array.extend ( arr1, var_args )

        Extends an array with another array, element, or "array like" object. + within f.Returns

        false if any element fails the test.
        code »<VALUE> goog.array.extend ( arr1, var_args )

        Extends an array with another array, element, or "array like" object. This function operates 'in-place', it does not create a new Array. Example: @@ -112,7 +118,8 @@ goog.array.extend(a, [0, 1]); a; // [0, 1] goog.array.extend(a, 2); - a; // [0, 1, 2]

        Parameters
        arr1: Array
        The array to modify.
        var_args: ...*
        The elements or arrays of elements to add to arr1.
        code »<T, S> goog.array.filter ( arr, f, opt_obj )!Array

        Calls a function for each element in an array, and if the function returns + a; // [0, 1, 2]

        Parameters
        arr1: Array.<VALUE>
        The array to modify.
        var_args: ...(Array.<VALUE>|VALUE)
        The elements or arrays of elements + to add to arr1.
        code »<T, S> goog.array.filter ( arr, f, opt_obj )!Array.<T>

        Calls a function for each element in an array, and if the function returns true adds the element to a new array. See http://tinyurl.com/developer-mozilla-org-array-filter

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array @@ -121,65 +128,70 @@ takes 3 arguments (the element, the index and the array) and must return a Boolean. If the return value is true the element is added to the result array. If it is false the element is not included.
        opt_obj: S=
        The object to be used as the value of 'this' - within f.
        Returns
        a new array in which only elements that passed the test are - present.
        code »<T, S> goog.array.find ( arr, f, opt_obj )T

        Search an array for the first element that satisfies a given condition and + within f.Returns

        a new array in which only elements that passed the test + are present.
        code »<T, S> goog.array.find ( arr, f, opt_obj )?T

        Search an array for the first element that satisfies a given condition and return that element.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: ?function(this: S, T, number, ?): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the array) and should return a boolean.
        opt_obj: S=
        An optional "this" context for the function.
        Returns
        The first array element that passes the test, or null if no - element is found.
        code »<T, S> goog.array.findIndex ( arr, f, opt_obj )number

        Search an array for the first element that satisfies a given condition and + element is found.

        code »<T, S> goog.array.findIndex ( arr, f, opt_obj )number

        Search an array for the first element that satisfies a given condition and return its index.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: ?function(this: S, T, number, ?): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the array) and should return a boolean.
        opt_obj: S=
        An optional "this" context for the function.
        Returns
        The index of the first array element that passes the test, - or -1 if no element is found.
        code »<T, S> goog.array.findIndexRight ( arr, f, opt_obj )number

        Search an array (in reverse order) for the last element that satisfies a + or -1 if no element is found.

        code »<T, S> goog.array.findIndexRight ( arr, f, opt_obj )number

        Search an array (in reverse order) for the last element that satisfies a given condition and return its index.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: ?function(this: S, T, number, ?): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the array) and should return a boolean.
        opt_obj: Object=
        An optional "this" context for the function.
        Returns
        The index of the last array element that passes the test, - or -1 if no element is found.
        code »<T, S> goog.array.findRight ( arr, f, opt_obj )T

        Search an array (in reverse order) for the last element that satisfies a + or -1 if no element is found.

        code »<T, S> goog.array.findRight ( arr, f, opt_obj )?T

        Search an array (in reverse order) for the last element that satisfies a given condition and return that element.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: ?function(this: S, T, number, ?): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the array) and should return a boolean.
        opt_obj: S=
        An optional "this" context for the function.
        Returns
        The last array element that passes the test, or null if no - element is found.

        Returns an array consisting of every argument with all arrays - expanded in-place recursively.

        Parameters
        var_args: ...*
        The values to flatten.
        Returns
        An array containing the flattened values.
        code »<T, S> goog.array.forEach ( arr, f, opt_obj )

        Calls a function for each element in an array. Skips holes in the array. + element is found.

        Returns an array consisting of every argument with all arrays + expanded in-place recursively.

        Parameters
        var_args: ...*
        The values to flatten.
        Returns
        An array containing the flattened values.
        code »<T, S> goog.array.forEach ( arr, f, opt_obj )

        Calls a function for each element in an array. Skips holes in the array. See http://tinyurl.com/developer-mozilla-org-array-foreach

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: ?function(this: S, T, number, ?): ?
        The function to call for every element. This function takes 3 arguments (the element, the index and the - array). The return value is ignored.
        opt_obj: S=
        The object to be used as the value of 'this' within f.
        code »<T, S> goog.array.forEachRight ( arr, f, opt_obj )

        Calls a function for each element in an array, starting from the last + array). The return value is ignored.

        opt_obj: S=
        The object to be used as the value of 'this' within f.
        code »<T, S> goog.array.forEachRight ( arr, f, opt_obj )

        Calls a function for each element in an array, starting from the last element rather than the first.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: ?function(this: S, T, number, ?): ?
        The function to call for every element. This function takes 3 arguments (the element, the index and the array). The return value is ignored.
        opt_obj: S=
        The object to be used as the value of 'this' - within f.
        code »goog.array.indexOf ( arr, obj, opt_fromIndex )number

        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. + within f.

        code »<T> goog.array.indexOf ( arr, obj, opt_fromIndex )number

        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 http://tinyurl.com/developer-mozilla-org-array-indexof

        Parameters
        arr: goog.array.ArrayLike
        The array to be searched.
        obj: *
        The object for which we are searching.
        opt_fromIndex: number=
        The index at which to start the search. If - omitted the search starts at index 0.
        Returns
        The index of the first matching array element.
        code »<T> goog.array.insert ( arr, obj )

        Pushes an item into an array, if it's not already in the array.

        Parameters
        arr: Array.<T>
        Array into which to insert the item.
        obj: T
        Value to add.
        code »goog.array.insertArrayAt ( arr, elementsToAdd, opt_i )

        Inserts at the given index of the array, all elements of another array.

        Parameters
        arr: goog.array.ArrayLike
        The array to modify.
        elementsToAdd: goog.array.ArrayLike
        The array of elements to add.
        opt_i: number=
        The index at which to insert the object. If omitted, - treated as 0. A negative index is counted from the end of the array.
        code »goog.array.insertAt ( arr, obj, opt_i )

        Inserts an object at the given index of the array.

        Parameters
        arr: goog.array.ArrayLike
        The array to modify.
        obj: *
        The object to insert.
        opt_i: number=
        The index at which to insert the object. If omitted, - treated as 0. A negative index is counted from the end of the array.
        code »<T> goog.array.insertBefore ( arr, obj, opt_obj2 )

        Inserts an object into an array before a specified object.

        Parameters
        arr: Array.<T>
        The array to modify.
        obj: T
        The object to insert.
        opt_obj2: T=
        The object before which obj should be inserted. If obj2 - is omitted or not found, obj is inserted at the end of the array.

        Whether the array is empty.

        Parameters
        arr: goog.array.ArrayLike
        The array to test.
        Returns
        true if empty.
        code »<T> goog.array.isSorted ( arr, opt_compareFn, opt_strict )boolean

        Tells if the array is sorted.

        Parameters
        arr: !Array.<T>
        The array.
        opt_compareFn: ?function(T, T): number=
        Function to compare the + See http://tinyurl.com/developer-mozilla-org-array-indexof
        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        The array to be searched.
        obj: T
        The object for which we are searching.
        opt_fromIndex: number=
        The index at which to start the search. If + omitted the search starts at index 0.
        Returns
        The index of the first matching array element.
        code »<T> goog.array.insert ( arr, obj )

        Pushes an item into an array, if it's not already in the array.

        Parameters
        arr: Array.<T>
        Array into which to insert the item.
        obj: T
        Value to add.
        code »goog.array.insertArrayAt ( arr, elementsToAdd, opt_i )

        Inserts at the given index of the array, all elements of another array.

        Parameters
        arr: goog.array.ArrayLike
        The array to modify.
        elementsToAdd: goog.array.ArrayLike
        The array of elements to add.
        opt_i: number=
        The index at which to insert the object. If omitted, + treated as 0. A negative index is counted from the end of the array.
        code »goog.array.insertAt ( arr, obj, opt_i )

        Inserts an object at the given index of the array.

        Parameters
        arr: goog.array.ArrayLike
        The array to modify.
        obj: *
        The object to insert.
        opt_i: number=
        The index at which to insert the object. If omitted, + treated as 0. A negative index is counted from the end of the array.
        code »<T> goog.array.insertBefore ( arr, obj, opt_obj2 )

        Inserts an object into an array before a specified object.

        Parameters
        arr: Array.<T>
        The array to modify.
        obj: T
        The object to insert.
        opt_obj2: T=
        The object before which obj should be inserted. If obj2 + is omitted or not found, obj is inserted at the end of the array.

        Whether the array is empty.

        Parameters
        arr: goog.array.ArrayLike
        The array to test.
        Returns
        true if empty.
        code »<T> goog.array.isSorted ( arr, opt_compareFn, opt_strict )boolean

        Tells if the array is sorted.

        Parameters
        arr: !Array.<T>
        The array.
        opt_compareFn: ?function(T, T): number=
        Function to compare the array elements. Should take 2 arguments to compare, and return a negative number, zero, or a positive number depending on whether the first argument is less - than, equal to, or greater than the second.
        opt_strict: boolean=
        If true no equal elements are allowed.
        Returns
        Whether the array is sorted.
        code »goog.array.lastIndexOf ( arr, obj, opt_fromIndex )number

        Returns the index of the last element of an array with a specified value, or + than, equal to, or greater than the second.

        opt_strict: boolean=
        If true no equal elements are allowed.
        Returns
        Whether the array is sorted.
        code »<T> goog.array.join ( var_args )!Array.<T>

        Returns a new array that contains the contents of all the arrays passed.

        Parameters
        var_args
        code »<T> goog.array.last ( array )T

        Returns the last element in an array without removing it. + Same as goog.array.peek.

        Parameters
        array: (Array.<T>|goog.array.ArrayLike)
        The array.
        Returns
        Last item in array.
        code »<T> goog.array.lastIndexOf ( arr, obj, opt_fromIndex )number

        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 http://tinyurl.com/developer-mozilla-org-array-lastindexof

        Parameters
        arr: goog.array.ArrayLike
        The array to be searched.
        obj: *
        The object for which we are searching.
        opt_fromIndex: ?number=
        The index at which to start the search. If - omitted the search starts at the end of the array.
        Returns
        The index of the last matching array element.
        code »<T, S> goog.array.map ( arr, f, opt_obj )!Array

        Calls a function for each element in an array and inserts the result into a + See http://tinyurl.com/developer-mozilla-org-array-lastindexof

        Parameters
        arr: (!Array.<T>|!goog.array.ArrayLike)
        The array to be searched.
        obj: T
        The object for which we are searching.
        opt_fromIndex: ?number=
        The index at which to start the search. If + omitted the search starts at the end of the array.
        Returns
        The index of the last matching array element.
        code »<THIS, VALUE, RESULT> goog.array.map ( arr, f, opt_obj )!Array.<RESULT>

        Calls a function for each element in an array and inserts the result into a new array. - See http://tinyurl.com/developer-mozilla-org-array-map

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array - like object over which to iterate.
        f: ?function(this: S, T, number, ?): ?
        The function to call for every - element. This function - takes 3 arguments (the element, the index and the array) and should - return something. The result will be inserted into a new array.
        opt_obj: S=
        The object to be used as the value of 'this' - within f.
        Returns
        a new array with the results from f.
        code »goog.array.peek ( array )*

        Returns the last element in an array without removing it.

        Parameters
        array: goog.array.ArrayLike
        The array.
        Returns
        Last item in array.
        code »goog.array.range ( startOrEnd, opt_end, opt_step )!Array.<number>

        Creates a range of numbers in an arithmetic progression. + See http://tinyurl.com/developer-mozilla-org-array-map

        Parameters
        arr: (Array.<VALUE>|goog.array.ArrayLike)
        Array or array like object + over which to iterate.
        f: function(this: THIS, VALUE, number, ?): RESULT
        The function to call + for every element. This function takes 3 arguments (the element, + the index and the array) and should return something. The result will be + inserted into a new array.
        opt_obj: THIS=
        The object to be used as the value of 'this' within f.
        Returns
        a new array with the results from f.
        code »goog.array.moveItem ( arr, fromIndex, toIndex )

        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.

        Parameters
        arr: !(Array|Arguments|{length: number})
        The array to modify.
        fromIndex: number
        Index of the item to move between 0 and + arr.length - 1.
        toIndex: number
        Target index between 0 and arr.length - 1.
        code »<T> goog.array.peek ( array )T

        Returns the last element in an array without removing it. + Same as goog.array.last.

        Parameters
        array: (Array.<T>|goog.array.ArrayLike)
        The array.
        Returns
        Last item in array.
        code »goog.array.range ( startOrEnd, opt_end, opt_step )!Array.<number>

        Creates a range of numbers in an arithmetic progression. Range takes 1, 2, or 3 arguments:

        @@ -191,7 +203,7 @@
              is provided. Otherwise, the start value is 0, and this is the end value.
        opt_end: number=
        The optional end value of the range.
        opt_step: number=
        The step size between range values. Defaults to 1 if opt_step is undefined or 0.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.
        code »<T, S, R> goog.array.reduce ( arr, f, val, opt_obj )R

        Passes every element of an array into a function and accumulates the result. + value.

        code »<T, S, R> goog.array.reduce ( arr, f, val, opt_obj )R

        Passes every element of an array into a function and accumulates the result. See http://tinyurl.com/developer-mozilla-org-array-reduce @@ -205,7 +217,7 @@ the value of the current array element, the current array index, and the array itself) function(previousValue, currentValue, index, array).

        val: ?
        The initial value to pass into the function on the first call.
        opt_obj: S=
        The object to be used as the value of 'this' - within f.Returns
        Result of evaluating f repeatedly across the values of the array.
        code »<T, S, R> goog.array.reduceRight ( arr, f, val, opt_obj )R

        Passes every element of an array into a function and accumulates the result, + within f.Returns

        Result of evaluating f repeatedly across the values of the array.
        code »<T, S, R> goog.array.reduceRight ( arr, f, val, opt_obj )R

        Passes every element of an array into a function and accumulates the result, starting from the last element and working towards the first. See http://tinyurl.com/developer-mozilla-org-array-reduceright @@ -221,28 +233,39 @@ array itself) function(previousValue, currentValue, index, array).

        val: ?
        The initial value to pass into the function on the first call.
        opt_obj: S=
        The object to be used as the value of 'this' within f.Returns
        Object returned as a result of evaluating f repeatedly across the - values of the array.

        Removes the first occurrence of a particular value from an array.

        Parameters
        arr: goog.array.ArrayLike
        Array from which to remove value.
        obj: *
        Object to remove.
        Returns
        True if an element was removed.

        Removes from an array the element at index i

        Parameters
        arr: goog.array.ArrayLike
        Array or array like object from which to - remove value.
        i: number
        The index to remove.
        Returns
        True if an element was removed.

        Removes all duplicates from an array (retaining only the first + values of the array.

        code »<T> goog.array.remove ( arr, obj )boolean

        Removes the first occurrence of a particular value from an array.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array from which to remove + value.
        obj: T
        Object to remove.
        Returns
        True if an element was removed.
        code »<T, S> goog.array.removeAllIf ( arr, f, opt_obj )number

        Removes all values that satisfy the given condition.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array + like object over which to iterate.
        f: ?function(this: S, T, number, ?): boolean
        The function to call + for every element. This function + takes 3 arguments (the element, the index and the array) and should + return a boolean.
        opt_obj: S=
        An optional "this" context for the function.
        Returns
        The number of items removed

        Removes from an array the element at index i

        Parameters
        arr: goog.array.ArrayLike
        Array or array like object from which to + remove value.
        i: number
        The index to remove.
        Returns
        True if an element was removed.
        code »<T> goog.array.removeDuplicates ( arr, opt_rv, opt_hashFn )

        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 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
        arr: goog.array.ArrayLike
        The array from which to remove duplicates.
        opt_rv: Array=
        An optional array in which to return the results, + Worstcase space: 2N (no dupes)
        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        The array from which to remove + duplicates.
        opt_rv: Array=
        An optional array in which to return the results, instead of performing the removal inplace. If specified, the original - array will remain unchanged.
        code »<T, S> goog.array.removeIf ( arr, f, opt_obj )boolean

        Removes the first value that satisfies the given condition.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array + array will remain unchanged.
        opt_hashFn: function(T): string=
        An optional function to use to + apply to every item in the array. This function should return a unique + value for each item in the array it should consider unique.
        code »<T, S> goog.array.removeIf ( arr, f, opt_obj )boolean

        Removes the first value that satisfies the given condition.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate.
        f: ?function(this: S, T, number, ?): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the array) and should - return a boolean.
        opt_obj: S=
        An optional "this" context for the function.
        Returns
        True if an element was removed.

        Returns an array consisting of the given value repeated N times.

        Parameters
        value: *
        The value to repeat.
        n: number
        The repeat count.
        Returns
        An array with the repeated value.
        code »<T> goog.array.rotate ( array, n )!Array.<T>

        Rotates an array in-place. After calling this method, the element at + return a boolean.

        opt_obj: S=
        An optional "this" context for the function.
        Returns
        True if an element was removed.
        code »<VALUE> goog.array.repeat ( value, n )!Array.<VALUE>

        Returns an array consisting of the given value repeated N times.

        Parameters
        value: VALUE
        The value to repeat.
        n: number
        The repeat count.
        Returns
        An array with the repeated value.
        code »<T> goog.array.rotate ( array, n )!Array.<T>

        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].

        Parameters
        array: !Array.<T>
        The array to rotate.
        n: number
        The amount to rotate.
        Returns
        The array.
        code »goog.array.shuffle ( arr, opt_randFn )

        Shuffles the values in the specified array using the Fisher-Yates in-place + rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].

        Parameters
        array: !Array.<T>
        The array to rotate.
        n: number
        The amount to rotate.
        Returns
        The array.
        code »goog.array.shuffle ( arr, opt_randFn )

        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. @@ -250,11 +273,11 @@ Runtime: O(n)

        Parameters
        arr: !Array
        The array to be shuffled.
        opt_randFn: function(): number=
        Optional random function to use for shuffling. Takes no arguments, and returns a random number on the interval [0, 1). - Defaults to Math.random() using JavaScript's built-in Math library.
        code »<T> goog.array.slice ( arr, start, opt_end )!Array.<T>

        Returns a new array from a segment of an array. This is a generic version of + Defaults to Math.random() using JavaScript's built-in Math library.

        code »<T> goog.array.slice ( arr, start, opt_end )!Array.<T>

        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.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        The array from which to copy a segment.
        start: number
        The index of the first element to copy.
        opt_end: number=
        The index after the last element to copy.
        Returns
        A new array containing the specified segment of the - original array.
        code »<T, S> goog.array.some ( arr, f, opt_obj )boolean

        Calls f for each element of an array. If any call returns true, some() + original array.

        code »<T, S> goog.array.some ( arr, f, opt_obj )boolean

        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. @@ -262,7 +285,7 @@ like object over which to iterate.

        f: ?function(this: S, T, number, ?): boolean
        The function to call for for every element. This function takes 3 arguments (the element, the index and the array) and should return a boolean.
        opt_obj: S=
        The object to be used as the value of 'this' - within f.Returns
        true if any element passes the test.
        code »<T> goog.array.sort ( arr, opt_compareFn )

        Sorts the specified array into ascending order. If no opt_compareFn is + within f.Returns

        true if any element passes the test.
        code »<T> goog.array.sort ( arr, opt_compareFn )

        Sorts the specified array into ascending order. If no opt_compareFn is specified, elements are compared using goog.array.defaultCompare, which compares the elements using the built in < and > operators. This will produce the expected behavior @@ -276,18 +299,18 @@ function by which the array is to be ordered. Should take 2 arguments to compare, and return a negative number, zero, or a positive number depending on whether the - first argument is less than, equal to, or greater than the second.

        code »goog.array.sortObjectsByKey ( arr, key, opt_compareFn )

        Sorts an array of objects by the specified object key and compare + first argument is less than, equal to, or greater than the second.

        code »goog.array.sortObjectsByKey ( arr, key, opt_compareFn )

        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 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
        arr: Array.<Object>
        An array of objects to sort.
        key: string
        The object key to sort by.
        opt_compareFn: Function=
        The function to use to compare key - values.
        code »goog.array.splice ( arr, index, howMany, var_args )!Array

        Adds or removes elements from an array. This is a generic version of Array + values.

        code »<T> goog.array.splice ( arr, index, howMany, var_args )!Array.<T>

        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.

        Parameters
        arr: goog.array.ArrayLike
        The array to modify.
        index: (number|undefined)
        The index at which to start changing the + such as the arguments object.
        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        The array to modify.
        index: (number|undefined)
        The index at which to start changing the array. If not defined, treated as 0.
        howMany: number
        How many elements to remove (0 means no removal. A value below 0 is treated as zero and so is any other non number. Numbers - are floored).
        var_args: ...*
        Optional, additional elements to insert into the - array.
        Returns
        the removed elements.
        code »<T> goog.array.stableSort ( arr, opt_compareFn )

        Sorts the specified array into ascending order in a stable way. If no + are floored).

        var_args: ...T
        Optional, additional elements to insert into the + array.
        Returns
        the removed elements.
        code »<T> goog.array.stableSort ( arr, opt_compareFn )

        Sorts the specified array into ascending order in a stable way. If no opt_compareFn is specified, elements are compared using goog.array.defaultCompare, which compares the elements using the built in < and > operators. This will produce the expected behavior @@ -298,10 +321,11 @@ by which the array is to be ordered. Should take 2 arguments to compare, and return a negative number, zero, or a positive number depending on whether the first argument is less than, equal to, or greater than the - second.

        Converts an object to an array.

        Parameters
        object: goog.array.ArrayLike
        The object to convert to an array.
        Returns
        The object converted into an array. If object has a + second.
        code »<T> goog.array.toArray ( object )!Array.<T>

        Converts an object to an array.

        Parameters
        object: (Array.<T>|goog.array.ArrayLike)
        The object to convert to an + array.
        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.
        code »<T, S> goog.array.toObject ( arr, keyFunc, opt_obj )!Object.<T>

        Creates a new object built from the provided array and the key-generation + have a length property, an empty array will be returned.

        code »<T, S> goog.array.toObject ( arr, keyFunc, opt_obj )!Object.<T>

        Creates a new object built from the provided array and the key-generation function.

        Parameters
        arr: (Array.<T>|goog.array.ArrayLike)
        Array or array like object over which to iterate whose elements will be the values in the new object.
        keyFunc: ?function(this: S, T, number, ?): string
        The function to call for every element. This function takes 3 arguments (the element, the @@ -309,9 +333,9 @@ key for the element in the new object. If the function returns the same key for more than one element, the value for that key is implementation-defined.
        opt_obj: S=
        The object to be used as the value of 'this' - within keyFunc.
        Returns
        The new object.
        code »goog.array.zip ( var_args )!Array

        Creates a new array for which the element at position i is an array of the + within keyFunc.Returns

        The new object.
        code »goog.array.zip ( var_args )!Array

        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 http://docs.python.org/library/functions.html#zip

        Parameters
        var_args: ...!goog.array.ArrayLike
        Arrays to be combined.
        Returns
        A new array of arrays created from provided arrays.

        Global Properties

        Reference to the original Array.prototype.

        \ No newline at end of file + This is similar to the zip() function in Python. See http://docs.python.org/library/functions.html#zip
        Parameters
        var_args: ...!goog.array.ArrayLike
        Arrays to be combined.
        Returns
        A new array of arrays created from provided arrays.

        Global Properties

        Reference to the original Array.prototype.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_asserts.html b/docs/namespace_goog_asserts.html index 5841b61..2c97fd2 100644 --- a/docs/namespace_goog_asserts.html +++ b/docs/namespace_goog_asserts.html @@ -1,12 +1,14 @@ -goog.asserts

        Namespace goog.asserts

        code »

        Classes

        goog.asserts.AssertionError
        Error object for failed assertions.
        Show:

        Global Functions

        code »goog.asserts.assert ( condition, opt_message, var_args )*

        Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is - true.

        Parameters
        condition: *
        The condition to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value of the condition.
        Throws
        goog.asserts.AssertionError
        When the condition evaluates to false.
        code »goog.asserts.assertArray ( value, opt_message, var_args )!Array

        Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a non-null array.
        Throws
        goog.asserts.AssertionError
        When the value is not an array.
        code »goog.asserts.assertBoolean ( value, opt_message, var_args )boolean

        Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a boolean when asserts are - enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a boolean.
        code »goog.asserts.assertFunction ( value, opt_message, var_args )!Function

        Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a function when asserts - enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a function.
        code »<T> goog.asserts.assertInstanceof ( value, type, opt_message, var_args )!T

        Checks if the value is an instance of the user-defined type if +goog.asserts

        Namespace goog.asserts

        code »

        Classes

        goog.asserts.AssertionError
        Error object for failed assertions.
        Show:

        Global Functions

        The default error handler.

        Parameters
        e: !goog.asserts.AssertionError
        The exception to be handled.
        code »<T> goog.asserts.assert ( condition, opt_message, var_args )T

        Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is + true.

        Parameters
        condition: T
        The condition to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value of the condition.
        Throws
        goog.asserts.AssertionError
        When the condition evaluates to false.
        code »goog.asserts.assertArray ( value, opt_message, var_args )!Array

        Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a non-null array.
        Throws
        goog.asserts.AssertionError
        When the value is not an array.
        code »goog.asserts.assertBoolean ( value, opt_message, var_args )boolean

        Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a boolean when asserts are + enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a boolean.
        code »goog.asserts.assertElement ( value, opt_message, var_args )!Element

        Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, likely to be a DOM Element when asserts are + enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a boolean.
        code »goog.asserts.assertFunction ( value, opt_message, var_args )!Function

        Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a function when asserts + enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a function.
        code »<T> goog.asserts.assertInstanceof ( value, type, opt_message, var_args )!T

        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.

        Parameters
        value: *
        The value to check.
        type: function(new: T, ...)
        A user-defined constructor.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Throws
        goog.asserts.AssertionError
        When the value is not an instance of - type.
        code »goog.asserts.assertNumber ( value, opt_message, var_args )number

        Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a number when asserts enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a number.
        code »goog.asserts.assertObject ( value, opt_message, var_args )!Object

        Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a non-null object.
        Throws
        goog.asserts.AssertionError
        When the value is not an object.
        code »goog.asserts.assertString ( value, opt_message, var_args )string

        Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a string when asserts enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a string.
        code »goog.asserts.doAssertFailure_ ( defaultMessage, defaultArgs, givenMessage, givenArgs )

        Throws an exception with the given message and "Assertion failed" prefixed - onto it.

        Parameters
        defaultMessage: string
        The message to use if givenMessage is empty.
        defaultArgs: Array
        The substitution arguments for defaultMessage.
        givenMessage: (string|undefined)
        Message supplied by the caller.
        givenArgs: Array
        The substitution arguments for givenMessage.
        Throws
        goog.asserts.AssertionError
        When the value is not a number.
        code »goog.asserts.fail ( opt_message, var_args )

        Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case + type.

        code »goog.asserts.assertNumber ( value, opt_message, var_args )number

        Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a number when asserts enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a number.
        code »goog.asserts.assertObject ( value, opt_message, var_args )!Object

        Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a non-null object.
        Throws
        goog.asserts.AssertionError
        When the value is not an object.

        Checks that no enumerable keys are present in Object.prototype. Such keys + would break most code that use for (var ... in ...) loops.

        code »goog.asserts.assertString ( value, opt_message, var_args )string

        Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.

        Parameters
        value: *
        The value to check.
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Returns
        The value, guaranteed to be a string when asserts enabled.
        Throws
        goog.asserts.AssertionError
        When the value is not a string.
        code »goog.asserts.doAssertFailure_ ( defaultMessage, defaultArgs, givenMessage, givenArgs )

        Throws an exception with the given message and "Assertion failed" prefixed + onto it.

        Parameters
        defaultMessage: string
        The message to use if givenMessage is empty.
        defaultArgs: Array
        The substitution arguments for defaultMessage.
        givenMessage: (string|undefined)
        Message supplied by the caller.
        givenArgs: Array
        The substitution arguments for givenMessage.
        Throws
        goog.asserts.AssertionError
        When the value is not a number.

        The handler responsible for throwing or logging assertion errors.

        code »goog.asserts.fail ( opt_message, var_args )

        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: @@ -17,4 +19,6 @@ default: goog.assert.fail('Unrecognized type: ' + type); // We have only 2 types - "default:" section is unreachable code. } -

        Parameters
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Throws
        goog.asserts.AssertionError
        Failure.

        Compiler Constants

        \ No newline at end of file +
        Parameters
        opt_message: string=
        Error message in case of failure.
        var_args: ...*
        The items to substitute into the failure message.
        Throws
        goog.asserts.AssertionError
        Failure.

        Sets a custom error handler that can be used to customize the behavior of + assertion failures, for example by turning all assertion failures into log + messages.

        Parameters
        errorHandler

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_async.html b/docs/namespace_goog_async.html new file mode 100644 index 0000000..75fad2a --- /dev/null +++ b/docs/namespace_goog_async.html @@ -0,0 +1,14 @@ +goog.async

        Namespace goog.async

        code »
        Show:

        Global Functions

        code »<SCOPE> goog.async.nextTick ( callback, opt_context )

        Fires the provided callbacks as soon as possible after the current JS + execution context. setTimeout(…, 0) takes at least 4ms when called from + within another setTimeout(…, 0) for legacy reasons. + + This will not schedule the callback as a microtask (i.e. a task that can + preempt user input or networking callbacks). It is meant to emulate what + setTimeout(_, 0) would do if it were not throttled. If you desire microtask + behavior, use goog.Promise instead.

        Parameters
        callback: function(this: SCOPE)
        Callback function to fire as soon as + possible.
        opt_context: SCOPE=
        Object in whose scope to call the listener.
        code »<THIS> goog.async.run ( callback, opt_context )

        Fires the provided callback just before the current callstack unwinds, or as + soon as possible after the current JS execution context.

        Parameters
        callback
        opt_context: THIS=
        Object to use as the "this value" when calling + the provided function.

        Throw an item without interrupting the current execution context. For + example, if processing a group of items in a loop, sometimes it is useful + to report an error while still allowing the rest of the batch to be + processed.

        Parameters
        exception
        \ No newline at end of file diff --git a/docs/namespace_goog_async_nextTick.html b/docs/namespace_goog_async_nextTick.html new file mode 100644 index 0000000..26a5026 --- /dev/null +++ b/docs/namespace_goog_async_nextTick.html @@ -0,0 +1,11 @@ +goog.async.nextTick

        Namespace goog.async.nextTick.<SCOPE>

        code »

        Fires the provided callbacks as soon as possible after the current JS + execution context. setTimeout(…, 0) takes at least 4ms when called from + within another setTimeout(…, 0) for legacy reasons. + + This will not schedule the callback as a microtask (i.e. a task that can + preempt user input or networking callbacks). It is meant to emulate what + setTimeout(_, 0) would do if it were not throttled. If you desire microtask + behavior, use goog.Promise instead.

        Main

        <SCOPE> nextTick ( callback, opt_context )
        Parameters
        callback: function(this: SCOPE)
        Callback function to fire as soon as + possible.
        opt_context: SCOPE=
        Object in whose scope to call the listener.
        Show:

        Global Functions

        Determines the best possible implementation to run a function as soon as + the JS event loop is idle.

        Returns
        The "setImmediate" implementation.

        Cache for the setImmediate implementation.

        code »goog.async.nextTick.wrapCallback_ ( callback )function()

        Helper function that is overrided to protect callbacks with entry point + monitor if the application monitors entry points.

        Parameters
        callback: function()
        Callback function to fire as soon as possible.
        Returns
        The wrapped callback.
        \ No newline at end of file diff --git a/docs/namespace_goog_async_run.html b/docs/namespace_goog_async_run.html new file mode 100644 index 0000000..80a1ddc --- /dev/null +++ b/docs/namespace_goog_async_run.html @@ -0,0 +1,9 @@ +goog.async.run

        Namespace goog.async.run.<THIS>

        code »

        Fires the provided callback just before the current callstack unwinds, or as + soon as possible after the current JS execution context.

        Main

        <THIS> run ( callback, opt_context )
        Parameters
        callback
        opt_context: THIS=
        Object to use as the "this value" when calling + the provided function.

        Classes

        Show:

        Global Functions

        Forces goog.async.run to use nextTick instead of Promise. + + This should only be done in unit tests. It's useful because MockClock + replaces nextTick, but not the browser Promise implementation, so it allows + Promise-based code to be tested with MockClock.

        Initializes the function to use to process the work queue.

        Run any pending goog.async.run work items. This function is not intended + for general use, but for use by entry point handlers to run items ahead of + goog.async.nextTick.

        Reset the event queue.

        The function used to schedule work asynchronousely.

        Global Properties

        \ No newline at end of file diff --git a/docs/namespace_goog_debug.html b/docs/namespace_goog_debug.html index 351be28..45efc3d 100644 --- a/docs/namespace_goog_debug.html +++ b/docs/namespace_goog_debug.html @@ -1 +1,27 @@ -goog.debug

        Namespace goog.debug

        code »

        Classes

        goog.debug.Error
        Base class for custom error objects.
        Show:
        \ No newline at end of file +goog.debug

        Namespace goog.debug

        code »

        Interfaces

        Classes

        goog.debug.Error
        Base class for custom error objects.
        goog.debug.LogBuffer
        Creates the log buffer.
        goog.debug.LogRecord
        LogRecord objects are used to pass logging requests between + the logging framework and individual log Handlers.
        goog.debug.Logger
        The Logger is an object used for logging debug messages.
        Show:

        Type Definitions

        A message value that can be handled by a Logger. + + Functions are treated like callbacks, but are only called when the event's + log level is enabled. This is useful for logging messages that are expensive + to construct.

        Global Functions

        code »goog.debug.catchErrors ( logFunc, opt_cancel, opt_target )

        Catches onerror events fired by windows and similar objects.

        Parameters
        logFunc: function(Object)
        The function to call with the error + information.
        opt_cancel: boolean=
        Whether to stop the error from reaching the + browser.
        opt_target: Object=
        Object that fires onerror events.
        code »goog.debug.deepExpose ( obj, opt_showFn )string

        Creates a string representing a given primitive or object, and for an + object, all its properties and nested objects. WARNING: If an object is + given, it and all its nested objects will be modified. To detect reference + cycles, this method identifies objects using goog.getUid() which mutates the + object.

        Parameters
        obj: *
        Object to expose.
        opt_showFn: boolean=
        Also show properties that are functions (by + default, functions are omitted).
        Returns
        A string representation of obj.
        code »goog.debug.enhanceError ( err, opt_message )!Error

        Converts an object to an Error if it's a String, + adds a stacktrace if there isn't one, + and optionally adds an extra message.

        Parameters
        err: (Error|string)
        the original thrown object or string.
        opt_message: string=
        optional additional message to add to the + error.
        Returns
        If err is a string, it is used to create a new Error, + which is enhanced and returned. Otherwise err itself is enhanced + and returned.
        code »goog.debug.expose ( obj, opt_showFn )string

        Creates a string representing an object and all its properties.

        Parameters
        obj: (Object|null|undefined)
        Object to expose.
        opt_showFn: boolean=
        Show the functions as well as the properties, + default is false.
        Returns
        The string representation of obj.

        Recursively outputs a nested array as a string.

        Parameters
        arr: Array
        The array.
        Returns
        String representing nested array.

        Exposes an exception that has been caught by a try...catch and outputs the + error with a stack trace.

        Parameters
        err: Object
        Error object or string.
        opt_fn: Function=
        Optional function to start stack trace from.
        Returns
        Details of exception.

        Resolves functions to their names. Resolved function names will be cached.

        Gets a function name

        Parameters
        fn: Function
        Function to get name of.
        Returns
        Function's name.
        Parameters
        fn: Function
        The function to start getting the trace from.

        Gets the current stack trace, either starting from the caller or starting + from a specified function that's currently on the call stack.

        Parameters
        opt_fn: Function=
        Optional function to start getting the trace from. + If not provided, defaults to the function that called this.
        Returns
        Stack trace.

        Private helper for getStacktrace().

        Parameters
        fn: Function
        Function to start getting the trace from.
        visited: Array
        List of functions visited so far.
        Returns
        Stack trace starting from function fn.

        Gets the current stack trace. Simple and iterative - doesn't worry about + catching circular references or getting the args.

        Parameters
        opt_depth: number=
        Optional maximum depth to trace back to.
        Returns
        A string with the function names of all functions in the + stack, separated by \n.

        Makes whitespace visible by replacing it with printable characters. + This is useful in finding diffrences between the expected and the actual + output strings of a testcase.

        Parameters
        string: string
        whose whitespace needs to be made visible.
        Returns
        string whose whitespace is made visible.

        Normalizes the error/exception object between browsers.

        Parameters
        err: Object
        Raw error object.
        Returns
        Normalized error object.

        Set a custom function name resolver.

        Parameters
        resolver: function(Function): string
        Resolves functions to their + names.

        Global Properties

        Max length of stack to try and output

        Hash map for storing function names that have already been looked up.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_debug_LogManager.html b/docs/namespace_goog_debug_LogManager.html new file mode 100644 index 0000000..4d2a933 --- /dev/null +++ b/docs/namespace_goog_debug_LogManager.html @@ -0,0 +1,10 @@ +goog.debug.LogManager

        Namespace goog.debug.LogManager

        code »

        There is a single global LogManager object that is used to maintain a set of + shared state about Loggers and log services. This is loosely based on the + java class java.util.logging.LogManager.

        Show:

        Global Functions

        Creates a function that can be passed to goog.debug.catchErrors. The function + will log all reported errors using the given logger.

        Parameters
        opt_logger: goog.debug.Logger=
        The logger to log the errors to. + Defaults to the root logger.
        Returns
        The created function.

        Creates the named logger. Will also create the parents of the named logger + if they don't yet exist.

        Parameters
        name: string
        The name of the logger.
        Returns
        The named logger.

        Finds a named logger.

        Parameters
        name: string
        A name for the logger. This should be a dot-separated + name and should normally be based on the package name or class name of the + subsystem, such as goog.net.BrowserChannel.
        Returns
        The named logger.

        Returns all the loggers.

        Returns
        Map of logger names to logger + objects.

        Returns the root of the logger tree namespace, the logger with the empty + string as its name.

        Returns
        The root logger.

        Initializes the LogManager if not already initialized.

        Global Properties

        Map of logger names to logger objects.

        The root logger which is the root of the logger tree.

        \ No newline at end of file diff --git a/docs/namespace_goog_debug_entryPointRegistry.html b/docs/namespace_goog_debug_entryPointRegistry.html new file mode 100644 index 0000000..c76becc --- /dev/null +++ b/docs/namespace_goog_debug_entryPointRegistry.html @@ -0,0 +1,17 @@ +goog.debug.entryPointRegistry

        Namespace goog.debug.entryPointRegistry

        code »
        Show:

        Global Functions

        Configures a monitor to wrap all entry points. + + Entry points that have already been registered are immediately wrapped by + the monitor. When an entry point is registered in the future, it will also + be wrapped by the monitor when it is registered.

        Parameters
        monitor: !goog.debug.EntryPointMonitor
        An entry point monitor.

        Register an entry point with this module. + + The entry point will be instrumented when a monitor is passed to + goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the + entry point is instrumented immediately.

        Parameters
        callback: function(!Function)
        A callback function which is called + with a transforming function to instrument the entry point. The callback + is responsible for wrapping the relevant entry point with the + transforming function.

        Try to unmonitor all the entry points that have already been registered. If + an entry point is registered in the future, it will not be wrapped by the + monitor when it is registered. Note that this may fail if the entry points + have additional wrapping.

        Parameters
        monitor: !goog.debug.EntryPointMonitor
        The last monitor to wrap + the entry points.
        Throws
        Error
        If the monitor is not the most recently configured monitor.

        Global Properties

        Whether goog.debug.entryPointRegistry.monitorAll has ever been called. + Checking this allows the compiler to optimize out the registrations.

        Monitors that should wrap all the entry points.

        An array of entry point callbacks.

        \ No newline at end of file diff --git a/docs/namespace_goog_defineClass.html b/docs/namespace_goog_defineClass.html new file mode 100644 index 0000000..e0d1d13 --- /dev/null +++ b/docs/namespace_goog_defineClass.html @@ -0,0 +1,16 @@ +goog.defineClass

        Namespace goog.defineClass

        code »

        Creates a restricted form of a Closure "class": + - from the compiler's perspective, the instance returned from the + constructor is sealed (no new properties may be added). This enables + better checks. + - the compiler will rewrite this definition to a form that is optimal + for type checking and optimization (initially this will be a more + traditional form).

        Main

        defineClass ( superClass, def )!Function
        Parameters
        superClass: Function
        The superclass, Object or null.
        def: goog.defineClass.ClassDescriptor
        An object literal describing the + the class. It may have the following properties: + "constructor": the constructor function + "statics": an object literal containing methods to add to the constructor + as "static" methods or a function that will receive the constructor + function as its only parameter to which static properties can + be added. + all other properties are added to the prototype.
        Returns
        The class constructor.
        Show:

        Type Definitions

        code »goog.defineClass.ClassDescriptor : (!Object|{constructor: !Function}|{constructor: !Function, statics: (Object|function(Function): void)})
        No description.

        Global Functions

        Parameters
        target: !Object
        The object to add properties to.
        source: !Object
        The object to copy properites from.

        If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is + defined, this function will wrap the constructor in a function that seals the + results of the provided constructor function.

        Parameters
        ctr: !Function
        The constructor whose results maybe be sealed.
        superClass: Function
        The superclass constructor.
        Returns
        The replacement constructor.

        Global Properties

        The names of the fields that are defined on Object.prototype.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_disposable.html b/docs/namespace_goog_disposable.html new file mode 100644 index 0000000..bedb313 --- /dev/null +++ b/docs/namespace_goog_disposable.html @@ -0,0 +1 @@ +goog.disposable

        Namespace goog.disposable

        code »

        Interfaces

        goog.disposable.IDisposable
        Interface for a disposable object.
        Show:
        \ No newline at end of file diff --git a/docs/namespace_goog_dom.html b/docs/namespace_goog_dom.html new file mode 100644 index 0000000..8a76354 --- /dev/null +++ b/docs/namespace_goog_dom.html @@ -0,0 +1,232 @@ +goog.dom

        Namespace goog.dom

        code »

        Classes

        goog.dom.DomHelper
        Create an instance of a DOM helper with a new document object.

        Enumerations

        goog.dom.BrowserFeature
        Enum of browser capabilities.
        goog.dom.NodeType
        Constants for the nodeType attribute in the Node interface.
        goog.dom.TagName
        Enum of all html tag names specified by the W3C HTML4.01 and HTML5 + specifications.
        Show:

        Type Definitions

        Typedef for use with goog.dom.createDom and goog.dom.append.

        Global Functions

        code »goog.dom.$ ( element )Element
        Deprecated: Use goog.dom.getElement instead.

        Alias for getElement.

        Parameters
        element: (string|Element)
        Element ID or a DOM node.
        Returns
        The element with the given ID, or the node passed in.
        code »goog.dom.$$ ( opt_tag, opt_class, opt_el ){length: number}
        Deprecated: Use goog.dom.getElementsByTagNameAndClass instead.

        Alias for getElementsByTagNameAndClass.

        Parameters
        opt_tag: ?string=
        Element tag name.
        opt_class: ?string=
        Optional class name.
        opt_el: Element=
        Optional element to look in.
        Returns
        Array-like list of elements (only a length + property and numerical indices are guaranteed to exist).
        code »goog.dom.$dom ( tagName, opt_attributes, var_args )!Element
        Deprecated: Use goog.dom.createDom instead.

        Alias for createDom.

        Parameters
        tagName: string
        Tag to create.
        opt_attributes: (string|Object)=
        If object, then a map of name-value + pairs for attributes. If a string, then this is the className of the new + element.
        var_args: ...(Object|string|Array|NodeList)
        Further DOM nodes or + strings for text nodes. If one of the var_args is an array, its + children will be added as childNodes instead.
        Returns
        Reference to a DOM node.
        code »goog.dom.append ( parent, var_args )

        Appends a node with text or other nodes.

        Parameters
        parent: !Node
        The node to append nodes to.
        var_args: ...goog.dom.Appendable
        The things to append to the node. + If this is a Node it is appended as is. + If this is a string then a text node is appended. + If this is an array like object then fields 0 to length - 1 are appended.
        code »goog.dom.appendChild ( parent, child )

        Appends a child to a node.

        Parameters
        parent: Node
        Parent.
        child: Node
        Child.
        code »goog.dom.append_ ( doc, parent, args, startIndex )

        Appends a node with text or other nodes.

        Parameters
        doc: !Document
        The document to create new nodes in.
        parent: !Node
        The node to append nodes to.
        args: !Arguments
        The values to add. See goog.dom.append.
        startIndex: number
        The index of the array to start from.

        Determines if the given node can contain children, intended to be used for + HTML generation. + + IE natively supports node.canHaveChildren but has inconsistent behavior. + Prior to IE8 the base tag allows children and in IE9 all nodes return true + for canHaveChildren. + + In practice all non-IE browsers allow you to add children to any node, but + the behavior is inconsistent: + +

        +   var a = document.createElement('br');
        +   a.appendChild(document.createTextNode('foo'));
        +   a.appendChild(document.createTextNode('bar'));
        +   console.log(a.childNodes.length);  // 2
        +   console.log(a.innerHTML);  // Chrome: "", IE9: "foobar", FF3.5: "foobar"
        + 
        + + For more information, see: + http://dev.w3.org/html5/markup/syntax.html#syntax-elements + + TODO(user): Rename shouldAllowChildren() ?
        Parameters
        node: Node
        The node to check.
        Returns
        Whether the node can contain children.

        Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and + fast W3C Selectors API.

        Parameters
        parent: !(Element|Document)
        The parent document object.
        Returns
        whether or not we can use parent.querySelector* APIs.

        Compares the document order of two nodes, returning 0 if they are the same + node, a negative number if node1 is before node2, and a positive number if + node2 is before node1. Note that we compare the order the tags appear in the + document so in the tree text the B node is considered to be + before the I node.

        Parameters
        node1: Node
        The first node to compare.
        node2: Node
        The second node to compare.
        Returns
        0 if the nodes are the same node, a negative number if node1 + is before node2, and a positive number if node2 is before node1.

        Utility function to compare the position of two nodes, when + textNode's parent is an ancestor of node. If this entry + condition is not met, this function will attempt to reference a null object.

        Parameters
        textNode: Node
        The textNode to compare.
        node: Node
        The node to compare.
        Returns
        -1 if node is before textNode, +1 otherwise.

        Utility function to compare the position of two nodes known to be non-equal + siblings.

        Parameters
        node1: Node
        The first node to compare.
        node2: Node
        The second node to compare.
        Returns
        -1 if node1 is before node2, +1 otherwise.
        code »goog.dom.contains ( parent, descendant )boolean

        Whether a node contains another node.

        Parameters
        parent: Node
        The node that should contain the other node.
        descendant: Node
        The node to test presence of.
        Returns
        Whether the parent node contains the descendent node.
        code »goog.dom.createDom ( tagName, opt_attributes, var_args )!Element

        Returns a dom node with a set of attributes. This function accepts varargs + for subsequent nodes to be added. Subsequent nodes will be added to the + first node as childNodes. + + So: + createDom('div', null, createDom('p'), createDom('p')); + would return a div with two child paragraphs

        Parameters
        tagName: string
        Tag to create.
        opt_attributes: (Object|Array.<string>|string)=
        If object, then a map + of name-value pairs for attributes. If a string, then this is the + className of the new element. If an array, the elements will be joined + together as the className of the new element.
        var_args: ...(Object|string|Array|NodeList)
        Further DOM nodes or + strings for text nodes. If one of the var_args is an array or NodeList,i + its elements will be added as childNodes instead.
        Returns
        Reference to a DOM node.

        Helper for createDom.

        Parameters
        doc: !Document
        The document to create the DOM in.
        args: !Arguments
        Argument object passed from the callers. See + goog.dom.createDom for details.
        Returns
        Reference to a DOM node.

        Creates a new element.

        Parameters
        name: string
        Tag name.
        Returns
        The new element.
        code »goog.dom.createTable ( rows, columns, opt_fillWithNbsp )!Element

        Create a table.

        Parameters
        rows: number
        The number of rows in the table. Must be >= 1.
        columns: number
        The number of columns in the table. Must be >= 1.
        opt_fillWithNbsp: boolean=
        If true, fills table entries with nsbps.
        Returns
        The created table.
        code »goog.dom.createTable_ ( doc, rows, columns, fillWithNbsp )!Element

        Create a table.

        Parameters
        doc: !Document
        Document object to use to create the table.
        rows: number
        The number of rows in the table. Must be >= 1.
        columns: number
        The number of columns in the table. Must be >= 1.
        fillWithNbsp: boolean
        If true, fills table entries with nsbps.
        Returns
        The created table.

        Creates a new text node.

        Parameters
        content: (number|string)
        Content.
        Returns
        The new text node.

        Find the deepest common ancestor of the given nodes.

        Parameters
        var_args: ...Node
        The nodes to find a common ancestor of.
        Returns
        The common ancestor of the nodes, or null if there is none. + null will only be returned if two or more of the nodes are from different + documents.

        Finds the first descendant node that matches the filter function, using + a depth first search. This function offers the most general purpose way + of finding a matching element. You may also wish to consider + goog.dom.query which can express many matching criteria using + CSS selector expressions. These expressions often result in a more + compact representation of the desired result.

        Parameters
        root: Node
        The root of the tree to search.
        p: function(Node): boolean
        The filter function.
        Returns
        The found node or undefined if none is found.

        Finds all the descendant nodes that match the filter function, using a + a depth first search. This function offers the most general-purpose way + of finding a set of matching elements. You may also wish to consider + goog.dom.query which can express many matching criteria using + CSS selector expressions. These expressions often result in a more + compact representation of the desired result.

        Parameters
        root: Node
        The root of the tree to search.
        p: function(Node): boolean
        The filter function.
        Returns
        The found nodes or an empty array if none are found.
        code »goog.dom.findNodes_ ( root, p, rv, findOne )boolean

        Finds the first or all the descendant nodes that match the filter function, + using a depth first search.

        Parameters
        root: Node
        The root of the tree to search.
        p: function(Node): boolean
        The filter function.
        rv: !Array
        The found nodes are added to this array.
        findOne: boolean
        If true we exit after the first found node.
        Returns
        Whether the search is complete or not. True in case findOne + is true and the node is found. False otherwise.

        Flattens an element. That is, removes it and replace it with its children. + Does nothing if the element is not in the document.

        Parameters
        element: Element
        The element to flatten.
        Returns
        The original element, detached from the document + tree, sans children; or undefined, if the element was not in the document + to begin with.

        Determines the active element in the given document.

        Parameters
        doc: Document
        The document to look in.
        Returns
        The active element.
        code »goog.dom.getAncestor ( element, matcher, opt_includeNode, opt_maxSearchSteps )Node

        Walks up the DOM hierarchy returning the first ancestor that passes the + matcher function.

        Parameters
        element: Node
        The DOM node to start with.
        matcher: function(Node): boolean
        A function that returns true if the + passed node matches the desired criteria.
        opt_includeNode: boolean=
        If true, the node itself is included in + the search (the first call to the matcher will pass startElement as + the node to test).
        opt_maxSearchSteps: number=
        Maximum number of levels to search up the + dom.
        Returns
        DOM node that matched the matcher, or null if there was + no match.
        code »goog.dom.getAncestorByClass ( element, className )Element

        Walks up the DOM hierarchy returning the first ancestor that has the passed + class name. If the passed element matches the specified criteria, the + element itself is returned.

        Parameters
        element: Node
        The DOM node to start with.
        className: string
        The class name to match.
        Returns
        The first ancestor that matches the passed criteria, or + null if none match.
        code »goog.dom.getAncestorByTagNameAndClass ( element, opt_tag, opt_class )Element

        Walks up the DOM hierarchy returning the first ancestor that has the passed + tag name and/or class name. If the passed element matches the specified + criteria, the element itself is returned.

        Parameters
        element: Node
        The DOM node to start with.
        opt_tag: ?(goog.dom.TagName|string)=
        The tag name to match (or + null/undefined to match only based on class name).
        opt_class: ?string=
        The class name to match (or null/undefined to + match only based on tag name).
        Returns
        The first ancestor that matches the passed criteria, or + null if no match is found.

        Returns an array containing just the element children of the given element.

        Parameters
        element: Element
        The element whose element children we want.
        Returns
        An array or array-like list of just the element + children of the given element.

        Gets the document object being used by the dom library.

        Returns
        Document object.

        Calculates the height of the document.

        Returns
        The height of the current document.

        Calculates the height of the document of the given window. + + Function code copied from the opensocial gadget api: + gadgets.window.adjustHeight(opt_height)

        Parameters
        win: Window
        The window whose document height to retrieve.
        Returns
        The height of the document of the given window.

        Gets the document scroll distance as a coordinate object.

        Returns
        Object with values 'x' and 'y'.

        Gets the document scroll element.

        Returns
        Scrolling element.

        Helper for getDocumentScrollElement.

        Parameters
        doc: !Document
        The document to get the scroll element for.
        Returns
        Scrolling element.

        Helper for getDocumentScroll.

        Parameters
        doc: !Document
        The document to get the scroll for.
        Returns
        Object with values 'x' and 'y'.

        Gets the DomHelper object for the document where the element resides.

        Parameters
        opt_element: (Node|Window)=
        If present, gets the DomHelper for this + element.
        Returns
        The DomHelper.

        Gets an element from the current document by element id. + + If an Element is passed in, it is returned.

        Parameters
        element: (string|Element)
        Element ID or a DOM node.
        Returns
        The element with the given ID, or the node passed in.
        code »goog.dom.getElementByClass ( className, opt_el )Element

        Returns the first element with the provided className.

        Parameters
        className: string
        the name of the class to look for.
        opt_el: (Element|Document)=
        Optional element to look in.
        Returns
        The first item with the class name provided.

        Gets an element by id from the given document (if present). + If an element is given, it is returned.

        Parameters
        doc
        element: (string|Element)
        Element ID or a DOM node.
        Returns
        The resulting element.
        code »goog.dom.getElementsByClass ( className, opt_el ){length: number}

        Returns a static, array-like list of the elements with the provided + className.

        Parameters
        className: string
        the name of the class to look for.
        opt_el: (Document|Element)=
        Optional element to look in.
        Returns
        The items found with the class name provided.
        code »goog.dom.getElementsByTagNameAndClass ( opt_tag, opt_class, opt_el ){length: number}

        Looks up elements by both tag and class name, using browser native functions + (querySelectorAll, getElementsByTagName or + getElementsByClassName) where possible. This function + is a useful, if limited, way of collecting a list of DOM elements + with certain characteristics. goog.dom.query offers a + more powerful and general solution which allows matching on CSS3 + selector expressions, but at increased cost in code size. If all you + need is particular tags belonging to a single class, this function + is fast and sleek. + + Note that tag names are case sensitive in the SVG namespace, and this + function converts opt_tag to uppercase for comparisons. For queries in the + SVG namespace you should use querySelector or querySelectorAll instead. + https://bugzilla.mozilla.org/show_bug.cgi?id=963870 + https://bugs.webkit.org/show_bug.cgi?id=83438

        Parameters
        opt_tag: ?string=
        Element tag name.
        opt_class: ?string=
        Optional class name.
        opt_el: (Document|Element)=
        Optional element to look in.
        Returns
        Array-like list of elements (only a length + property and numerical indices are guaranteed to exist).
        code »goog.dom.getElementsByTagNameAndClass_ ( doc, opt_tag, opt_class, opt_el ){length: number}

        Helper for getElementsByTagNameAndClass.

        Parameters
        doc: !Document
        The document to get the elements in.
        opt_tag: ?string=
        Element tag name.
        opt_class: ?string=
        Optional class name.
        opt_el: (Document|Element)=
        Optional element to look in.
        Returns
        Array-like list of elements (only a length + property and numerical indices are guaranteed to exist).

        Returns the first child node that is an element.

        Parameters
        node: Node
        The node to get the first child element of.
        Returns
        The first child node of node that is an element.

        Cross-browser function for getting the document element of a frame or iframe.

        Parameters
        frame: Element
        Frame element.
        Returns
        The frame content document.

        Cross-browser function for getting the window of a frame or iframe.

        Parameters
        frame: Element
        Frame element.
        Returns
        The window associated with the given frame.

        Returns the last child node that is an element.

        Parameters
        node: Node
        The node to get the last child element of.
        Returns
        The last child node of node that is an element.

        Returns the first node that is an element in the specified direction, + starting with node.

        Parameters
        node: Node
        The node to get the next element from.
        forward: boolean
        Whether to look forwards or backwards.
        Returns
        The first element.

        Returns the first next sibling that is an element.

        Parameters
        node: Node
        The node to get the next sibling element of.
        Returns
        The next sibling of node that is an element.

        Returns the next node in source order from the given node.

        Parameters
        node: Node
        The node.
        Returns
        The next node in the DOM tree, or null if this was the last + node.
        code »goog.dom.getNodeAtOffset ( parent, offset, opt_result )Node

        Returns the node at a given offset in a parent node. If an object is + provided for the optional third parameter, the node and the remainder of the + offset will stored as properties of this object.

        Parameters
        parent: Node
        The parent node.
        offset: number
        The offset into the parent node.
        opt_result: Object=
        Object to be used to store the return value. The + return value will be stored in the form {node: Node, remainder: number} + if this object is provided.
        Returns
        The node at the given offset.

        Returns the text length of the text contained in a node, without markup. This + is equivalent to the selection length if the node was selected, or the number + of cursor movements to traverse the node. Images & BRs take one space. New + lines are ignored.

        Parameters
        node: Node
        The node whose text content length is being calculated.
        Returns
        The length of node's text content.
        code »goog.dom.getNodeTextOffset ( node, opt_offsetParent )number

        Returns the text offset of a node relative to one of its ancestors. The text + length is the same as the length calculated by goog.dom.getNodeTextLength.

        Parameters
        node: Node
        The node whose offset is being calculated.
        opt_offsetParent: Node=
        The node relative to which the offset will + be calculated. Defaults to the node's owner document's body.
        Returns
        The text offset.

        Gets the outerHTML of a node, which islike innerHTML, except that it + actually contains the HTML of the node itself.

        Parameters
        element: Element
        The element to get the HTML of.
        Returns
        The outerHTML of the given element.

        Returns the owner document for a node.

        Parameters
        node: (Node|Window)
        The node to get the document for.
        Returns
        The document owning the node.
        Deprecated: Use goog.dom.getDocumentScroll instead.

        Gets the page scroll distance as a coordinate object.

        Parameters
        opt_window: Window=
        Optional window element to test.
        Returns
        Object with values 'x' and 'y'.

        Returns an element's parent, if it's an Element.

        Parameters
        element: Element
        The DOM element.
        Returns
        The parent, or null if not an Element.

        Gives the current devicePixelRatio. + + By default, this is the value of window.devicePixelRatio (which should be + preferred if present). + + If window.devicePixelRatio is not present, the ratio is calculated with + window.matchMedia, if present. Otherwise, gives 1.0. + + Some browsers (including Chrome) consider the browser zoom level in the pixel + ratio, so the value may change across multiple calls.

        Returns
        The number of actual pixels per virtual pixel.

        Returns the first previous sibling that is an element.

        Parameters
        node: Node
        The node to get the previous sibling element of.
        Returns
        The first previous sibling of node that is + an element.

        Returns the previous node in source order from the given node.

        Parameters
        node: Node
        The node.
        Returns
        The previous node in the DOM tree, or null if this was the + first node.

        Returns the text content of the current node, without markup. + + Unlike getTextContent this method does not collapse whitespaces + or normalize lines breaks.

        Parameters
        node: Node
        The node from which we are getting content.
        Returns
        The raw text content.

        Gets an element by id, asserting that the element is found. + + This is used when an element is expected to exist, and should fail with + an assertion error if it does not (if assertions are enabled).

        Parameters
        id: string
        Element ID.
        Returns
        The element with the given ID, if it exists.

        Ensures an element with the given className exists, and then returns the + first element with the provided className.

        Parameters
        className: string
        the name of the class to look for.
        opt_root: (!Element|!Document)=
        Optional element or document to look + in.
        Returns
        The first item with the class name provided.
        Throws
        goog.asserts.AssertionError
        Thrown if no element is found.

        Helper function for getRequiredElementHelper functions, both static and + on DomHelper. Asserts the element with the given id exists.

        Parameters
        doc
        id
        Returns
        The element with the given ID, if it exists.

        Returns the text content of the current node, without markup and invisible + symbols. New lines are stripped and whitespace is collapsed, + such that each character would be visible. + + In browsers that support it, innerText is used. Other browsers attempt to + simulate it via node traversal. Line breaks are canonicalized in IE.

        Parameters
        node: Node
        The node from which we are getting content.
        Returns
        The text content.
        code »goog.dom.getTextContent_ ( node, buf, normalizeWhitespace )

        Recursive support function for text content retrieval.

        Parameters
        node: Node
        The node from which we are getting content.
        buf: Array
        string buffer.
        normalizeWhitespace: boolean
        Whether to normalize whitespace.

        Gets the dimensions of the viewport. + + Gecko Standards mode: + docEl.clientWidth Width of viewport excluding scrollbar. + win.innerWidth Width of viewport including scrollbar. + body.clientWidth Width of body element. + + docEl.clientHeight Height of viewport excluding scrollbar. + win.innerHeight Height of viewport including scrollbar. + body.clientHeight Height of document. + + Gecko Backwards compatible mode: + docEl.clientWidth Width of viewport excluding scrollbar. + win.innerWidth Width of viewport including scrollbar. + body.clientWidth Width of viewport excluding scrollbar. + + docEl.clientHeight Height of document. + win.innerHeight Height of viewport including scrollbar. + body.clientHeight Height of viewport excluding scrollbar. + + IE6/7 Standards mode: + docEl.clientWidth Width of viewport excluding scrollbar. + win.innerWidth Undefined. + body.clientWidth Width of body element. + + docEl.clientHeight Height of viewport excluding scrollbar. + win.innerHeight Undefined. + body.clientHeight Height of document element. + + IE5 + IE6/7 Backwards compatible mode: + docEl.clientWidth 0. + win.innerWidth Undefined. + body.clientWidth Width of viewport excluding scrollbar. + + docEl.clientHeight 0. + win.innerHeight Undefined. + body.clientHeight Height of viewport excluding scrollbar. + + Opera 9 Standards and backwards compatible mode: + docEl.clientWidth Width of viewport excluding scrollbar. + win.innerWidth Width of viewport including scrollbar. + body.clientWidth Width of viewport excluding scrollbar. + + docEl.clientHeight Height of document. + win.innerHeight Height of viewport including scrollbar. + body.clientHeight Height of viewport excluding scrollbar. + + WebKit: + Safari 2 + docEl.clientHeight Same as scrollHeight. + docEl.clientWidth Same as innerWidth. + win.innerWidth Width of viewport excluding scrollbar. + win.innerHeight Height of the viewport including scrollbar. + frame.innerHeight Height of the viewport exluding scrollbar. + + Safari 3 (tested in 522) + + docEl.clientWidth Width of viewport excluding scrollbar. + docEl.clientHeight Height of viewport excluding scrollbar in strict mode. + body.clientHeight Height of viewport excluding scrollbar in quirks mode.

        Parameters
        opt_window: Window=
        Optional window element to test.
        Returns
        Object with values 'width' and 'height'.

        Helper for getViewportSize.

        Parameters
        win: Window
        The window to get the view port size for.
        Returns
        Object with values 'width' and 'height'.
        code »goog.dom.getWindow ( opt_doc )!Window

        Gets the window object associated with the given document.

        Parameters
        opt_doc: Document=
        Document object to get window for.
        Returns
        The window associated with the given document.
        code »goog.dom.getWindow_ ( doc )!Window

        Helper for getWindow.

        Parameters
        doc: !Document
        Document object to get window for.
        Returns
        The window associated with the given document.

        Returns true if the element has a bounding rectangle that would be visible + (i.e. its width and height are greater than zero).

        Parameters
        element: Element
        Element to check.
        Returns
        Whether the element has a non-zero bounding rectangle.

        Returns true if the element has a specified tab index.

        Parameters
        element: Element
        Element to check.
        Returns
        Whether the element has a specified tab index.

        Converts an HTML string into a document fragment. The string must be + sanitized in order to avoid cross-site scripting. For example + goog.dom.htmlToDocumentFragment('&lt;img src=x onerror=alert(0)&gt;') + triggers an alert in all browsers, even if the returned document fragment + is thrown away immediately.

        Parameters
        htmlString: string
        The HTML string to convert.
        Returns
        The resulting document fragment.

        Helper for htmlToDocumentFragment.

        Parameters
        doc: !Document
        The document.
        htmlString: string
        The HTML string to convert.
        Returns
        The resulting document fragment.
        code »goog.dom.insertChildAt ( parent, child, index )

        Insert a child at a given index. If index is larger than the number of child + nodes that the parent currently has, the node is inserted as the last child + node.

        Parameters
        parent: Element
        The element into which to insert the child.
        child: Node
        The element to insert.
        index: number
        The index at which to insert the new child node. Must + not be negative.

        Inserts a new node after an existing reference node (i.e. as the next + sibling). If the reference node has no parent, then does nothing.

        Parameters
        newNode: Node
        Node to insert.
        refNode: Node
        Reference node to insert after.

        Inserts a new node before an existing reference node (i.e. as the previous + sibling). If the reference node has no parent, then does nothing.

        Parameters
        newNode: Node
        Node to insert.
        refNode: Node
        Reference node to insert before.

        Returns true if the browser is in "CSS1-compatible" (standards-compliant) + mode, false otherwise.

        Returns
        True if in CSS1-compatible mode.

        Returns true if the browser is in "CSS1-compatible" (standards-compliant) + mode, false otherwise.

        Parameters
        doc: Document
        The document to check.
        Returns
        True if in CSS1-compatible mode.

        Whether the object looks like an Element.

        Parameters
        obj: ?
        The object being tested for Element likeness.
        Returns
        Whether the object looks like an Element.

        Returns true if the element can be focused, i.e. it has a tab index that + allows it to receive keyboard focus (tabIndex >= 0), or it is an element + that natively supports keyboard focus.

        Parameters
        element: Element
        Element to check.
        Returns
        Whether the element allows keyboard focus.

        Returns true if the element has a tab index that allows it to receive + keyboard focus (tabIndex >= 0), false otherwise. Note that some elements + natively support keyboard focus, even if they have no tab index.

        Parameters
        element: Element
        Element to check.
        Returns
        Whether the element has a tab index that allows keyboard + focus.

        Whether the object looks like a DOM node.

        Parameters
        obj: ?
        The object being tested for node likeness.
        Returns
        Whether the object looks like a DOM node.

        Returns true if the object is a NodeList. To qualify as a NodeList, + the object must have a numeric length property and an item function (which + has type 'string' on IE for some reason).

        Parameters
        val: Object
        Object to test.
        Returns
        Whether the object is a NodeList.

        Returns true if the element's tab index allows the element to be focused.

        Parameters
        element: Element
        Element to check.
        Returns
        Whether the element's tab index allows focus.

        Returns true if the specified value is a Window object. This includes the + global window for HTML pages, and iframe windows.

        Parameters
        obj: ?
        Variable to test.
        Returns
        Whether the variable is a window.

        Calculates a mediaQuery to check if the current device supports the + given actual to virtual pixel ratio.

        Parameters
        pixelRatio: number
        The ratio of actual pixels to virtual pixels.
        Returns
        pixelRatio if applicable, otherwise 0.

        Returns true if the element is focusable even when tabIndex is not set.

        Parameters
        element: Element
        Element to check.
        Returns
        Whether the element natively supports focus.

        Removes all the child nodes on a DOM node.

        Parameters
        node: Node
        Node to remove children from.

        Removes a node from its parent.

        Parameters
        node: Node
        The node to remove.
        Returns
        The node removed if removed; else, null.
        code »goog.dom.replaceNode ( newNode, oldNode )

        Replaces a node in the DOM tree. Will do nothing if oldNode has no + parent.

        Parameters
        newNode: Node
        Node to insert.
        oldNode: Node
        Node to replace.

        Enables or disables keyboard focus support on the element via its tab index. + Only elements for which goog.dom.isFocusableTabIndex returns true + (or elements that natively support keyboard focus, like form elements) can + receive keyboard focus. See http://go/tabindex for more info.

        Parameters
        element: Element
        Element whose tab index is to be changed.
        enable: boolean
        Whether to set or remove a tab index on the element + that supports keyboard focus.
        code »goog.dom.setProperties ( element, properties )

        Sets multiple properties on a node.

        Parameters
        element: Element
        DOM node to set properties on.
        properties: Object
        Hash of property:value pairs.

        Sets the text content of a node, with cross-browser support.

        Parameters
        node: Node
        The node to change the text content of.
        text: (string|number)
        The value that should replace the node's content.

        Global Properties

        Whether we know the compatibility mode at compile time.

        Map of attributes that should be set using + element.setAttribute(key, val) instead of element[key] = val. Used + by goog.dom.setProperties.

        Map of tags which have predefined values with regard to whitespace.

        Map of tags whose content to ignore when calculating text length.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_dom_vendor.html b/docs/namespace_goog_dom_vendor.html new file mode 100644 index 0000000..b8e21e7 --- /dev/null +++ b/docs/namespace_goog_dom_vendor.html @@ -0,0 +1,4 @@ +goog.dom.vendor

        Namespace goog.dom.vendor

        code »
        Show:

        Global Functions

        Parameters
        eventType: string
        An event type.
        Returns
        A lower-cased vendor prefixed event type.
        code »goog.dom.vendor.getPrefixedPropertyName ( propertyName, opt_object )?string
        Parameters
        propertyName: string
        A property name.
        opt_object: !Object=
        If provided, we verify if the property exists in + the object.
        Returns
        A vendor prefixed property name, or null if it does not + exist.

        Returns the JS vendor prefix used in CSS properties. Different vendors + use different methods of changing the case of the property names.

        Returns
        The JS vendor prefix or null if there is none.

        Returns the vendor prefix used in CSS properties.

        Returns
        The vendor prefix or null if there is none.
        \ No newline at end of file diff --git a/docs/namespace_goog_events.html b/docs/namespace_goog_events.html new file mode 100644 index 0000000..a072eb5 --- /dev/null +++ b/docs/namespace_goog_events.html @@ -0,0 +1,97 @@ +goog.events

        Namespace goog.events

        code »

        Interfaces

        goog.events.Listenable
        A listenable interface.
        goog.events.ListenableKey
        An interface that describes a single registered listener.

        Classes

        goog.events.BrowserEvent
        Accepts a browser event object and creates a patched, cross browser event + object.
        goog.events.Event
        A base class for event objects, so that they can support preventDefault and + stopPropagation.
        goog.events.EventId
        A templated class that is used when registering for events.
        goog.events.EventTarget
        An implementation of goog.events.Listenable with full W3C + EventTarget-like support (capture/bubble mechanism, stopping event + propagation, preventing default actions).
        goog.events.Listener
        Simple class that stores information about a listener
        goog.events.ListenerMap
        Creates a new listener map.

        Enumerations

        goog.events.BrowserFeature
        Enum of browser capabilities.
        goog.events.CaptureSimulationMode
        No Description.
        goog.events.EventType
        Constants for event names.
        goog.events.KeyCodes
        Key codes for common characters.
        Show:

        Type Definitions

        A typedef for event like objects that are dispatchable via the + goog.events.dispatchEvent function. strings are treated as the type for a + goog.events.Event. Objects are treated as an extension of a new + goog.events.Event with the type property of the object being used as the type + of the Event.

        Global Functions

        Dispatches an event (or event like object) and calls all listeners + listening for events of this type. The type of the event is decided by the + type property on the event object. + + If any of the listeners returns false OR calls preventDefault then this + function will return false. If one of the capture listeners calls + stopPropagation, then the bubble listeners won't fire.

        Parameters
        src: goog.events.Listenable
        The event target.
        e: goog.events.EventLike
        Event object.
        Returns
        If anyone called preventDefault on the event object (or + if any of the handlers returns false) this will also return false. + If there are no handlers, or if all handlers return true, this returns + true.

        Provides a nice string showing the normalized event objects public members

        Parameters
        e: Object
        Event Object.
        Returns
        String of the public members of the normalized event object.
        code »goog.events.fireListener ( listener, eventObject )boolean

        Fires a listener with a set of arguments

        Parameters
        listener: goog.events.Listener
        The listener object to call.
        eventObject: Object
        The event object to pass to the listener.
        Returns
        Result of listener.
        code »goog.events.fireListeners ( obj, type, capture, eventObject )boolean

        Fires an object's listeners of a particular type and phase

        Parameters
        obj: Object
        Object whose listeners to call.
        type: (string|!goog.events.EventId)
        Event type.
        capture: boolean
        Which event phase.
        eventObject: Object
        Event object to be passed to listener.
        Returns
        True if all listeners returned true else false.
        code »goog.events.fireListeners_ ( obj, type, capture, eventObject )boolean

        Fires an object's listeners of a particular type and phase.

        Parameters
        obj: Object
        Object whose listeners to call.
        type: (string|!goog.events.EventId)
        Event type.
        capture: boolean
        Which event phase.
        eventObject: Object
        Event object to be passed to listener.
        Returns
        True if all listeners returned true else false.
        code »<EVENTOBJ> goog.events.getListener ( src, type, listener, opt_capt, opt_handler )goog.events.ListenableKey

        Gets the goog.events.Listener for the event or null if no such listener is + in use.

        Parameters
        src: (EventTarget|goog.events.Listenable)
        The target from + which to get listeners.
        type: (?string|!goog.events.EventId.<EVENTOBJ>)
        The type of the event.
        listener: (function(EVENTOBJ): ?|{handleEvent: function(?): ?}|null)
        The + listener function to get.
        opt_capt: boolean=
        In DOM-compliant browsers, this determines + whether the listener is fired during the + capture or bubble phase of the event.
        opt_handler: Object=
        Element in whose scope to call the listener.
        Returns
        the found listener or null if not found.
        Parameters
        src: EventTarget
        The source object.
        Returns
        A listener map for the given + source object, or null if none exists.

        Gets the listeners for a given object, type and capture phase.

        Parameters
        obj: Object
        Object to get listeners for.
        type: (string|!goog.events.EventId)
        Event type.
        capture: boolean
        Capture phase?.
        Returns
        Array of listener objects.

        Returns a string with on prepended to the specified type. This is used for IE + which expects "on" to be prepended. This function caches the string in order + to avoid extra allocations in steady state.

        Parameters
        type: string
        Event type.
        Returns
        The type string with 'on' prepended.

        Helper function for returning a proxy function.

        Returns
        A new or reused function object.
        Deprecated: This returns estimated count, now that Closure no longer + stores a central listener registry. We still return an estimation + to keep existing listener-related tests passing. In the near future, + this function will be removed.

        Gets the total number of listeners currently in the system.

        Returns
        Number of listeners.

        Creates a unique event id.

        Parameters
        identifier: string
        The identifier.
        Returns
        A unique identifier.

        Returns a prefixed event name for the current browser.

        Parameters
        eventName: string
        The name of the event.
        Returns
        The prefixed event name.

        Handles an event and dispatches it to the correct listeners. This + function is a proxy for the real listener the user specified.

        Parameters
        listener: goog.events.Listener
        The listener object.
        opt_evt: Event=
        Optional event object that gets passed in via the + native event handlers.
        Returns
        Result of the event handler.
        code »goog.events.hasListener ( obj, opt_type, opt_capture )boolean

        Returns whether an event target has any active listeners matching the + specified signature. If either the type or capture parameters are + unspecified, the function will match on the remaining criteria.

        Parameters
        obj: (EventTarget|goog.events.Listenable)
        Target to get + listeners for.
        opt_type: (string|!goog.events.EventId)=
        Event type.
        opt_capture: boolean=
        Whether to check for capture or bubble-phase + listeners.
        Returns
        Whether an event target has one or more listeners matching + the requested type and/or capture phase.

        This is used to check if an IE event has already been handled by the Closure + system so we do not do the Closure pass twice for a bubbling event.

        Parameters
        e: Event
        The IE browser event.
        Returns
        True if the event object has been marked.
        code »<T, EVENTOBJ> goog.events.listen ( src, type, listener, opt_capt, opt_handler )goog.events.Key

        Adds an event listener for a specific event on a native event + target (such as a DOM element) or an object that has implemented + goog.events.Listenable. A listener can only be added once + to an object and if it is added again the key for the listener is + returned. Note that if the existing listener is a one-off listener + (registered via listenOnce), it will no longer be a one-off + listener after a call to listen().

        Parameters
        src: (EventTarget|goog.events.Listenable)
        The node to listen + to events on.
        type: (string|Array.<string>|!goog.events.EventId.<EVENTOBJ>|!Array)
        Event type or array of event types.
        listener: (function(this: T, EVENTOBJ): ?|{handleEvent: function(?): ?}|null)
        Callback method, or an object with a handleEvent function. + WARNING: passing an Object is now softly deprecated.
        opt_capt: boolean=
        Whether to fire in capture phase (defaults to + false).
        opt_handler: T=
        Element in whose scope to call the listener.
        Returns
        Unique key for the listener.
        code »<T, EVENTOBJ> goog.events.listenOnce ( src, type, listener, opt_capt, opt_handler )goog.events.Key

        Adds an event listener for a specific event on a native event + target (such as a DOM element) or an object that has implemented + goog.events.Listenable. After the event has fired the event + listener is removed from the target. + + If an existing listener already exists, listenOnce will do + nothing. In particular, if the listener was previously registered + via listen(), listenOnce() will not turn the listener into a + one-off listener. Similarly, if there is already an existing + one-off listener, listenOnce does not modify the listeners (it is + still a once listener).

        Parameters
        src: (EventTarget|goog.events.Listenable)
        The node to listen + to events on.
        type: (string|Array.<string>|!goog.events.EventId.<EVENTOBJ>|!Array)
        Event type or array of event types.
        listener: (function(this: T, EVENTOBJ): ?|{handleEvent: function(?): ?}|null)
        Callback method.
        opt_capt: boolean=
        Fire in capture phase?.
        opt_handler: T=
        Element in whose scope to call the listener.
        Returns
        Unique key for the listener.
        code »<T> goog.events.listenWithWrapper ( src, wrapper, listener, opt_capt, opt_handler )

        Adds an event listener with a specific event wrapper on a DOM Node or an + object that has implemented goog.events.Listenable. A listener can + only be added once to an object.

        Parameters
        src: (EventTarget|goog.events.Listenable)
        The target to + listen to events on.
        wrapper: goog.events.EventWrapper
        Event wrapper to use.
        listener: (function(this: T, ?): ?|{handleEvent: function(?): ?}|null)
        Callback method, or an object with a handleEvent function.
        opt_capt: boolean=
        Whether to fire in capture phase (defaults to + false).
        opt_handler: T=
        Element in whose scope to call the listener.
        code »goog.events.listen_ ( src, type, listener, callOnce, opt_capt, opt_handler )goog.events.ListenableKey

        Adds an event listener for a specific event on a native event + target. A listener can only be added once to an object and if it + is added again the key for the listener is returned. + + Note that a one-off listener will not change an existing listener, + if any. On the other hand a normal listener will change existing + one-off listener to become a normal listener.

        Parameters
        src: EventTarget
        The node to listen to events on.
        type: (string|!goog.events.EventId)
        Event type.
        listener: !Function
        Callback function.
        callOnce: boolean
        Whether the listener is a one-off + listener or otherwise.
        opt_capt: boolean=
        Whether to fire in capture phase (defaults to + false).
        opt_handler: Object=
        Element in whose scope to call the listener.
        Returns
        Unique key for the listener.

        This is used to mark the IE event object so we do not do the Closure pass + twice for a bubbling event.

        Parameters
        e: Event
        The IE browser event.

        Installs exception protection for the browser event entry point using the + given error handler.

        Parameters
        errorHandler: goog.debug.ErrorHandler
        Error handler with which to + protect the entry point.
        code »goog.events.removeAll ( obj, opt_type )number

        Removes all listeners from an object. You can also optionally + remove listeners of a particular type.

        Parameters
        obj: (Object|undefined)
        Object to remove listeners from. Must be an + EventTarget or a goog.events.Listenable.
        opt_type: (string|!goog.events.EventId)=
        Type of event to remove. + Default is all types.
        Returns
        Number of listeners removed.
        Deprecated: This doesn't do anything, now that Closure no longer + stores a central listener registry.

        Removes all native listeners registered via goog.events. Native + listeners are listeners on native browser objects (such as DOM + elements). In particular, goog.events.Listenable and + goog.events.EventTarget listeners will NOT be removed.

        Returns
        Number of listeners removed.
        code »<EVENTOBJ> goog.events.unlisten ( src, type, listener, opt_capt, opt_handler )?boolean

        Removes an event listener which was added with listen().

        Parameters
        src: (EventTarget|goog.events.Listenable)
        The target to stop + listening to events on.
        type: (string|Array.<string>|!goog.events.EventId.<EVENTOBJ>|!Array)
        Event type or array of event types to unlisten to.
        listener: (function(?): ?|{handleEvent: function(?): ?}|null)
        The + listener function to remove.
        opt_capt: boolean=
        In DOM-compliant browsers, this determines + whether the listener is fired during the capture or bubble phase of the + event.
        opt_handler: Object=
        Element in whose scope to call the listener.
        Returns
        indicating whether the listener was there to remove.

        Removes an event listener which was added with listen() by the key + returned by listen().

        Parameters
        key: goog.events.Key
        The key returned by listen() for this + event listener.
        Returns
        indicating whether the listener was there to remove.
        code »goog.events.unlistenWithWrapper ( src, wrapper, listener, opt_capt, opt_handler )

        Removes an event listener which was added with listenWithWrapper().

        Parameters
        src: (EventTarget|goog.events.Listenable)
        The target to stop + listening to events on.
        wrapper: goog.events.EventWrapper
        Event wrapper to use.
        listener: (function(?): ?|{handleEvent: function(?): ?}|null)
        The + listener function to remove.
        opt_capt: boolean=
        In DOM-compliant browsers, this determines + whether the listener is fired during the capture or bubble phase of the + event.
        opt_handler: Object=
        Element in whose scope to call the listener.
        Parameters
        listener: (Object|Function)
        The listener function or an + object that contains handleEvent method.
        Returns
        Either the original function or a function that + calls obj.handleEvent. If the same listener is passed to this + function more than once, the same function is guaranteed to be + returned.

        Global Properties

        Property name on a native event target for the listener map + associated with the event target.

        Expando property for listener function wrapper for Object with + handleEvent.

        Estimated count of total native listeners.

        Map of computed "on" strings for IE event types. Caching + this removes an extra object allocation in goog.events.listen which + improves IE6 performance.

        String used to prepend to IE event types.

        Counter to create unique event ids.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_functions.html b/docs/namespace_goog_functions.html new file mode 100644 index 0000000..a40a710 --- /dev/null +++ b/docs/namespace_goog_functions.html @@ -0,0 +1,30 @@ +goog.functions

        Namespace goog.functions

        code »
        Show:

        Global Functions

        Always returns false.

        Always returns NULL.

        Always returns true.

        code »goog.functions.and ( var_args )function(?): boolean

        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).

        Parameters
        var_args: ...Function
        A list of functions.
        Returns
        A function that ANDs its component + functions.
        code »<T> goog.functions.cacheReturnValue ( fn )!function(): T

        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.

        Parameters
        fn: !function(): T
        A function to lazily evaluate.
        Returns
        A wrapped version the function.
        code »<T> goog.functions.compose ( fn, var_args )function(?): T

        Creates the composition of the functions passed in. + For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).

        Parameters
        fn: function(?): T
        The final function.
        var_args: ...Function
        A list of functions.
        Returns
        The composition of all inputs.
        code »<T> goog.functions.constant ( retValue )function(): T

        Creates a function that always returns the same value.

        Parameters
        retValue: T
        The value to return.
        Returns
        The new function.
        code »goog.functions.create ( constructor, var_args )!Object

        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.

        Parameters
        constructor: !Function
        The constructor for the Object.
        var_args: ...*
        The arguments to be passed to the constructor.
        Returns
        A new instance of the class given in constructor.

        Creates a function that always throws an error with the given message.

        Parameters
        message: string
        The error message.
        Returns
        The error-throwing function.

        Creates a function that throws the given object.

        Parameters
        err: *
        An object to be thrown.
        Returns
        The error-throwing function.
        code »<T> goog.functions.identity ( opt_returnValue, var_args )T

        A simple function that returns the first argument of whatever is passed + into it.

        Parameters
        opt_returnValue: T=
        The single value that will be returned.
        var_args: ...*
        Optional trailing arguments. These are ignored.
        Returns
        The first argument passed in, or undefined if nothing was passed.
        code »goog.functions.lock ( f, opt_numArgs )!Function

        Given a function, create a function that keeps opt_numArgs arguments and + silently discards all additional arguments.

        Parameters
        f: Function
        The original function.
        opt_numArgs: number=
        The number of arguments to keep. Defaults to 0.
        Returns
        A version of f that only keeps the first opt_numArgs + arguments.
        code »goog.functions.not ( f )function(?): boolean

        Creates a function that returns the Boolean opposite of a provided function. + For example, (goog.functions.not(f))(x) is equivalent to !f(x).

        Parameters
        f: !Function
        The original function.
        Returns
        A function that delegates to f and returns + opposite.

        Creates a function that returns its nth argument.

        Parameters
        n: number
        The position of the return argument.
        Returns
        A new function.
        code »goog.functions.or ( var_args )function(?): boolean

        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).

        Parameters
        var_args: ...Function
        A list of functions.
        Returns
        A function that ORs its component + functions.

        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).

        Parameters
        var_args: ...Function
        A list of functions.
        Returns
        A function that calls all inputs in sequence.
        code »<T> goog.functions.withReturnValue ( f, retValue )function(?): T

        Given a function, create a new function that swallows its return value + and replaces it with a new one.

        Parameters
        f: Function
        A function.
        retValue: T
        A new return value.
        Returns
        A new function.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_iter.html b/docs/namespace_goog_iter.html index 22ae8f4..eacac6f 100644 --- a/docs/namespace_goog_iter.html +++ b/docs/namespace_goog_iter.html @@ -1,50 +1,127 @@ -goog.iter

        Namespace goog.iter

        code »

        Classes

        goog.iter.Iterator
        Class/interface for iterators.
        Show:

        Type Definitions

        code »goog.iter.Iterable : (goog.iter.Iterator|{length: number}|{__iterator__: ?})
        No description.

        Global Functions

        Takes zero or more iterators and returns one iterator that will iterate over - them in the order chained.

        Parameters
        var_args: ...goog.iter.Iterator
        Any number of iterator objects.
        Returns
        Returns a new iterator that will iterate over - all the given iterators' contents.

        Create an iterator to cycle over the iterable's elements indefinitely. - For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...

        Parameters
        iterable: !goog.iter.Iterable
        The iterable object.
        Returns
        An iterator that iterates indefinitely over - the values in iterable.
        code »<T> goog.iter.dropWhile ( iterable, f, opt_obj )!goog.iter.Iterator

        Builds a new iterator that iterates over the original, but skips elements as - long as a supplied function returns true.

        Parameters
        iterable: goog.iter.Iterable
        The iterator object.
        f: function(this: T, ?, undefined, ?): boolean
        The function to call for - every value. This function - takes 3 arguments (the value, undefined, and the iterator) and should - return a boolean.
        opt_obj: T=
        The object to be used as the value of 'this' within - f.
        Returns
        A new iterator that drops elements from the - original iterator as long as f is true.
        code »goog.iter.equals ( iterable1, iterable2 )boolean

        Iterates over 2 iterators and returns true if they contain the same sequence - of elements and have the same length.

        Parameters
        iterable1: goog.iter.Iterable
        The first iterable object.
        iterable2: goog.iter.Iterable
        The second iterable object.
        Returns
        true if the iterators contain the same sequence of - elements and have the same length.
        code »<T> goog.iter.every ( iterable, f, opt_obj )boolean

        Goes through the values in the iterator. Calls f for each these and if any of - them returns false this returns false (without checking the rest). If all - return true this will return true.

        Parameters
        iterable: goog.iter.Iterable
        The iterator object.
        f: function(this: T, ?, undefined, ?): boolean
        The function to call for - every value. This function - takes 3 arguments (the value, undefined, and the iterator) and should - return a boolean.
        opt_obj: T=
        The object to be used as the value of 'this' within - f.
        Returns
        true if every value passes the test.
        code »<T> goog.iter.filter ( iterable, f, opt_obj )!goog.iter.Iterator

        Calls a function for every element in the iterator, and if the function - returns true adds the element to a new iterator.

        Parameters
        iterable: goog.iter.Iterable
        The iterator to iterate over.
        f: function(this: T, ?, undefined, ?): boolean
        The function to call for - every element. This function - takes 3 arguments (the element, undefined, and the iterator) and should - return a boolean. If the return value is true the element will be - included in the returned iteror. If it is false the element is not - included.
        opt_obj: T=
        The object to be used as the value of 'this' within - f.
        Returns
        A new iterator in which only elements that - passed the test are present.
        code »<T> goog.iter.forEach ( iterable, f, opt_obj )

        Calls a function for each element in the iterator with the element of the - iterator passed as argument.

        Parameters
        iterable: goog.iter.Iterable
        The iterator to iterate - over. If the iterable is an object toIterator will be called on - it.
        f: function(this: T, ?, ?, ?): ?
        The function to call for every - element. This function - takes 3 arguments (the element, undefined, and the iterator) and the - return value is irrelevant. The reason for passing undefined as the - second argument is so that the same function can be used in - goog.array#forEach as well as others.
        opt_obj: T=
        The object to be used as the value of 'this' within - f.
        code »goog.iter.join ( iterable, deliminator )string

        Joins the values in a iterator with a delimiter.

        Parameters
        iterable: goog.iter.Iterable
        The iterator to get the values from.
        deliminator: string
        The text to put between the values.
        Returns
        The joined value string.
        code »<T> goog.iter.map ( iterable, f, opt_obj )!goog.iter.Iterator

        For every element in the iterator call a function and return a new iterator - with that value.

        Parameters
        iterable: goog.iter.Iterable
        The iterator to iterate over.
        f: function(this: T, ?, undefined, ?): ?
        The function to call for every - element. This function - takes 3 arguments (the element, undefined, and the iterator) and should - return a new value.
        opt_obj: T=
        The object to be used as the value of 'this' within - f.
        Returns
        A new iterator that returns the results of - applying the function to each element in the original iterator.
        code »goog.iter.nextOrValue ( iterable, defaultValue )*

        Advances the iterator to the next position, returning the given default value - instead of throwing an exception if the iterator has no more entries.

        Parameters
        iterable: goog.iter.Iterable
        The iterable object.
        defaultValue: *
        The value to return if the iterator is empty.
        Returns
        The next item in the iteration, or defaultValue if the iterator - was empty.

        Cartesian product of zero or more sets. Gives an iterator that gives every +goog.iter

        Namespace goog.iter

        code »

        Classes

        goog.iter.GroupByIterator_
        Implements the goog.iter.groupBy iterator.
        goog.iter.Iterator
        Class/interface for iterators.
        Show:

        Type Definitions

        code »goog.iter.Iterable : (goog.iter.Iterator|{length: number}|{__iterator__: ?})
        No description.

        Global Functions

        Creates an iterator that returns running totals from the numbers in + iterable. For example, the array [1, 2, 3, 4, 5] yields + 1 -> 3 -> 6 -> 10 -> 15.

        Parameters
        iterable: !goog.iter.Iterable.<number>
        The iterable of numbers to + accumulate.
        Returns
        A new iterator that returns the + numbers in the series.
        code »<VALUE> goog.iter.chain ( var_args )!goog.iter.Iterator.<VALUE>

        Takes zero or more iterables and returns one iterator that will iterate over + them in the order chained.

        Parameters
        var_args: ...(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        Any + number of iterable objects.
        Returns
        Returns a new iterator that will + iterate over all the given iterables' contents.
        code »<VALUE> goog.iter.chainFromIterable ( iterable )!goog.iter.Iterator.<VALUE>

        Takes a single iterable containing zero or more iterables and returns one + iterator that will iterate over each one in the order given.

        Parameters
        iterable: goog.iter.Iterable
        The iterable of iterables to chain.
        Returns
        Returns a new iterator that will + iterate over all the contents of the iterables contained within + iterable.
        code »<VALUE> goog.iter.combinations ( iterable, length )!goog.iter.Iterator

        Creates an iterator that returns combinations of elements from + 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
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable from which to generate combinations.
        length: number
        The length of each combination.
        Returns
        A new iterator containing + combinations from the iterable.

        Creates an iterator that returns combinations of elements from + 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
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to combine.
        length: number
        The length of each combination.
        Returns
        A new iterator containing + combinations from the iterable.
        code »<VALUE> goog.iter.compress ( iterable, selectors )!goog.iter.Iterator.<VALUE>

        Creates an iterator that filters 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
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to filter.
        selectors: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        An + iterable of items to be evaluated in a boolean context to determine if + the corresponding element in iterable should be included in the + result.
        Returns
        A new iterator that returns the + filtered values.
        code »<VALUE> goog.iter.consume ( iterable, count )!goog.iter.Iterator.<VALUE>

        Creates an iterator that is advanced 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
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to consume.
        count: number
        The number of elements to consume from the iterator.
        Returns
        An iterator advanced zero or more steps + ahead.
        code »goog.iter.count ( opt_start, opt_step )!goog.iter.Iterator.<number>

        Creates an iterator that counts indefinitely from a starting value.

        Parameters
        opt_start: number=
        The starting value. Default is 0.
        opt_step: number=
        The number to increment with between each call to + next. Negative and floating point numbers are allowed. Default is 1.
        Returns
        A new iterator that returns the values + in the series.
        code »<VALUE> goog.iter.cycle ( iterable )!goog.iter.Iterator.<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, ...

        Parameters
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable object.
        Returns
        An iterator that iterates indefinitely + over the values in iterable.
        code »<THIS, VALUE> goog.iter.dropWhile ( iterable, f, opt_obj )!goog.iter.Iterator.<VALUE>

        Builds a new iterator that iterates over the original, but skips elements as + long as a supplied function returns true.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + object.
        f: function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): boolean
        The function to call for every value. This function takes 3 arguments + (the value, undefined, and the iterator) and should return a boolean.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        A new iterator that drops elements from + the original iterator as long as f is true.
        code »<VALUE> goog.iter.enumerate ( iterable, opt_start )!goog.iter.Iterator

        Creates an iterator that returns arrays containing a count and an element + obtained from the given iterable.

        Parameters
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to enumerate.
        opt_start: number=
        Optional starting value. Default is 0.
        Returns
        A new iterator containing count/item + pairs.
        code »<VALUE> goog.iter.equals ( iterable1, iterable2 )boolean

        Iterates over two iterables and returns true if they contain the same + sequence of elements and have the same length.

        Parameters
        iterable1: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The first + iterable object.
        iterable2: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The second + iterable object.
        Returns
        true if the iterables contain the same sequence of elements + and have the same length.
        code »<THIS, VALUE> goog.iter.every ( iterable, f, opt_obj )boolean

        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.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + object.
        f: function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): boolean
        The function to call for every value. This function takes 3 arguments + (the value, undefined, and the iterator) and should return a boolean.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        true if every value passes the test.
        code »<THIS, VALUE> goog.iter.filter ( iterable, f, opt_obj )!goog.iter.Iterator.<VALUE>

        Calls a function for every element in the iterator, and if the function + returns true adds the element to a new iterator.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + to iterate over.
        f: function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): boolean
        The function to call for every element. This function takes 3 arguments + (the element, undefined, and the iterator) and should return a boolean. + If the return value is true the element will be included in the returned + iterator. If it is false the element is not included.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        A new iterator in which only elements + that passed the test are present.
        code »<THIS, VALUE> goog.iter.filterFalse ( iterable, f, opt_obj )!goog.iter.Iterator.<VALUE>

        Calls a function for every element in the iterator, and if the function + returns false adds the element to a new iterator.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + to iterate over.
        f: function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): boolean
        The function to call for every element. This function takes 3 arguments + (the element, undefined, and the iterator) and should return a boolean. + If the return value is false the element will be included in the returned + iterator. If it is true the element is not included.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        A new iterator in which only elements + that did not pass the test are present.
        code »<THIS, VALUE> goog.iter.forEach ( iterable, f, opt_obj )

        Calls a function for each element in the iterator with the element of the + iterator passed as argument.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + to iterate over. If the iterable is an object toIterator will be + called on it.
        f: (function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>)|function(this: THIS, number, undefined, goog.iter.Iterator.<VALUE>))
        The function to call for every element. This function takes 3 arguments + (the element, undefined, and the iterator) and the return value is + irrelevant. The reason for passing undefined as the second argument is + so that the same function can be used in goog.array#forEach as + well as others.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        code »<KEY, VALUE> goog.iter.groupBy ( iterable, opt_keyFunc )!goog.iter.Iterator

        Creates an iterator that returns arrays containing elements from the + 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
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to group.
        opt_keyFunc: function(VALUE): KEY=
        Optional function for + determining the key value for each group in the iterable. Default + is the identity function.
        Returns
        A new iterator that returns arrays of + consecutive key and groups.

        Checks an array for duplicate elements.

        Parameters
        arr: (Array.<VALUE>|goog.array.ArrayLike)
        The array to check for + duplicates.
        Returns
        True, if the array contains duplicates, false otherwise.
        code »<VALUE> goog.iter.join ( iterable, deliminator )string

        Joins the values in a iterator with a delimiter.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + to get the values from.
        deliminator: string
        The text to put between the values.
        Returns
        The joined value string.
        code »<VALUE> goog.iter.limit ( iterable, limitSize )!goog.iter.Iterator.<VALUE>

        Creates an iterator that returns the first limitSize elements from an + iterable. If this number is greater than the number of elements in the + iterable, all the elements are returned.

        Parameters
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to limit.
        limitSize: number
        The maximum number of elements to return.
        Returns
        A new iterator containing + limitSize elements.
        code »<THIS, VALUE, RESULT> goog.iter.map ( iterable, f, opt_obj )!goog.iter.Iterator.<RESULT>

        For every element in the iterator call a function and return a new iterator + with that value.

        Parameters
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterator to iterate over.
        f: function(this: THIS, VALUE, undefined, !goog.iter.Iterator.<VALUE>): RESULT
        The function to call for every element. This function takes 3 arguments + (the element, undefined, and the iterator) and should return a new value.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        A new iterator that returns the + results of applying the function to each element in the original + iterator.
        code »<VALUE> goog.iter.nextOrValue ( iterable, defaultValue )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.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterable + object.
        defaultValue: VALUE
        The value to return if the iterator is empty.
        Returns
        The next item in the iteration, or defaultValue if the + iterator was empty.
        code »<VALUE> goog.iter.permutations ( iterable, opt_length )!goog.iter.Iterator

        Creates an iterator that returns permutations of elements in + 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
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable from which to generate permutations.
        opt_length: number=
        Length of each permutation. If omitted, defaults + to the length of iterable.
        Returns
        A new iterator containing the + permutations of iterable.

        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]).

        Parameters
        var_args: ...!goog.array.ArrayLike
        Zero or more sets, as arrays.
        Returns
        An iterator that gives each n-tuple (as an - array).
        code »goog.iter.range ( startOrStop, opt_stop, opt_step )!goog.iter.Iterator

        Creates a new iterator that returns the values in a range. This function + ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).

        Parameters
        var_args: ...!goog.array.ArrayLike.<VALUE>
        Zero or more sets, as + arrays.
        Returns
        An iterator that gives each + n-tuple (as an array).
        code »goog.iter.range ( startOrStop, opt_stop, opt_step )!goog.iter.Iterator.<number>

        Creates a new iterator that returns the values in a range. This function can take 1, 2 or 3 arguments:

          range(5) same as range(0, 5, 1)
        @@ -53,26 +130,60 @@
              The start value if 2 or more arguments are provided.  If only one
              argument is used the start value is 0.
        opt_stop: number=
        The stop value. If left out then the first argument is used as the stop value.
        opt_step: number=
        The number to increment with between each call to - next. This can be negative.Returns
        A new iterator that returns the values in the - range.
        code »<T, V> goog.iter.reduce ( iterable, f, val, opt_obj )V

        Passes every element of an iterator into a function and accumulates the - result.

        Parameters
        iterable: goog.iter.Iterable
        The iterator to iterate over.
        f: function(this: T, V, ?): V
        The function to call for every - element. This function takes 2 arguments (the function's previous result - or the initial value, and the value of the current element). - function(previousValue, currentElement) : newValue.
        val: V
        The initial value to pass into the function on the first call.
        opt_obj: T=
        The object to be used as the value of 'this' - within f.
        Returns
        Result of evaluating f repeatedly across the values of - the iterator.
        code »<T> goog.iter.some ( iterable, f, opt_obj )boolean

        Goes through the values in the iterator. Calls f for each these and if any of - them returns true, this returns true (without checking the rest). If all - return false this will return false.

        Parameters
        iterable: goog.iter.Iterable
        The iterator object.
        f: function(this: T, ?, undefined, ?): boolean
        The function to call for - every value. This function - takes 3 arguments (the value, undefined, and the iterator) and should - return a boolean.
        opt_obj: T=
        The object to be used as the value of 'this' within - f.
        Returns
        true if any value passes the test.
        code »<T> goog.iter.takeWhile ( iterable, f, opt_obj )!goog.iter.Iterator

        Builds a new iterator that iterates over the original, but only as long as a - supplied function returns true.

        Parameters
        iterable: goog.iter.Iterable
        The iterator object.
        f: function(this: T, ?, undefined, ?): boolean
        The function to call for - every value. This function - takes 3 arguments (the value, undefined, and the iterator) and should - return a boolean.
        opt_obj: T=
        This is used as the 'this' object in f when called.
        Returns
        A new iterator that keeps elements in the - original iterator as long as the function is true.

        Converts the iterator to an array

        Parameters
        iterable: goog.iter.Iterable
        The iterator to convert to an array.
        Returns
        An array of the elements the iterator iterates over.

        Returns an iterator that knows how to iterate over the values in the object.

        Parameters
        iterable: goog.iter.Iterable
        If the object is an iterator it - will be returned as is. If the object has a __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.

        Global Properties

        Singleton Error object that is used to terminate iterations.

        \ No newline at end of file + next. This can be negative.Returns
        A new iterator that returns the values + in the range.
        code »<THIS, VALUE> goog.iter.reduce ( iterable, f, val, opt_obj )VALUE

        Passes every element of an iterator into a function and accumulates the + result.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + to iterate over.
        f: function(this: THIS, VALUE, VALUE): VALUE
        The function to call for + every element. This function takes 2 arguments (the function's previous + result or the initial value, and the value of the current element). + function(previousValue, currentElement) : newValue.
        val: VALUE
        The initial value to pass into the function on the first + call.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        Result of evaluating f repeatedly across the values of + the iterator.
        code »<VALUE> goog.iter.repeat ( value )!goog.iter.Iterator.<VALUE>

        Creates an iterator that returns the same object or value repeatedly.

        Parameters
        value: VALUE
        Any object or value to repeat.
        Returns
        A new iterator that returns the + repeated value.
        code »<VALUE> goog.iter.slice ( iterable, start, opt_end )!goog.iter.Iterator.<VALUE>

        Creates an iterator that returns a range of elements from an iterable. + Similar to goog.array#slice but does not support negative indexes.

        Parameters
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to slice.
        start: number
        The index of the first element to return.
        opt_end: number=
        The index after the last element to return. If + defined, must be greater than or equal to start.
        Returns
        A new iterator containing a slice of + the original.
        code »<THIS, VALUE> goog.iter.some ( iterable, f, opt_obj )boolean

        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.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + object.
        f: function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): boolean
        The function to call for every value. This function takes 3 arguments + (the value, undefined, and the iterator) and should return a boolean.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        true if any value passes the test.
        code »<THIS, RESULT> goog.iter.starMap ( iterable, f, opt_obj )!goog.iter.Iterator.<RESULT>

        Gives an iterator that gives the result of calling the given function + 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
        iterable: !goog.iter.Iterable
        The iterable of + iterables to iterate over.
        f: function(this: THIS, *): RESULT
        The function to call for every + element. This function takes N+2 arguments, where N represents the + number of items from the next element of the iterable. The two + additional arguments passed to the function are undefined and the + iterator itself. The function should return a new value.
        opt_obj: THIS=
        The object to be used as the value of 'this' within + f.
        Returns
        A new iterator that returns the + results of applying the function to each element in the original + iterator.
        code »<THIS, VALUE> goog.iter.takeWhile ( iterable, f, opt_obj )!goog.iter.Iterator.<VALUE>

        Builds a new iterator that iterates over the original, but only as long as a + supplied function returns true.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + object.
        f: function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): boolean
        The function to call for every value. This function takes 3 arguments + (the value, undefined, and the iterator) and should return a boolean.
        opt_obj: THIS=
        This is used as the 'this' object in f when called.
        Returns
        A new iterator that keeps elements in + the original iterator as long as the function is true.
        code »<VALUE> goog.iter.tee ( iterable, opt_num )!Array.<goog.iter.Iterator>

        Returns an array of iterators each of which can iterate over the values in + iterable without advancing the others.

        Parameters
        iterable: (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        The + iterable to tee.
        opt_num: number=
        The number of iterators to create. Default is 2.
        Returns
        An array of iterators.
        code »<VALUE> goog.iter.toArray ( iterable )!Array.<VALUE>

        Converts the iterator to an array

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        The iterator + to convert to an array.
        Returns
        An array of the elements the iterator iterates over.
        code »<VALUE> goog.iter.toIterator ( iterable )!goog.iter.Iterator.<VALUE>

        Returns an iterator that knows how to iterate over the values in the object.

        Parameters
        iterable: (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)
        If the + object is an iterator it will be returned as is. If the object has an + __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.
        code »<VALUE> goog.iter.zip ( var_args )!goog.iter.Iterator

        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 var_args. Once the shortest iterable is + exhausted, subsequent calls to next() will throw + goog.iter.StopIteration.

        Parameters
        var_args: ...(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        Any + number of iterable objects.
        Returns
        A new iterator that returns + arrays of elements from the provided iterables.
        code »<VALUE> goog.iter.zipLongest ( fillValue, var_args )!goog.iter.Iterator

        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 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
        fillValue: VALUE
        The object or value used to fill shorter iterables.
        var_args: ...(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)
        Any + number of iterable objects.
        Returns
        A new iterator that returns + arrays of elements from the provided iterables.

        Global Properties

        Singleton Error object that is used to terminate iterations.

        \ No newline at end of file diff --git a/docs/namespace_goog_json.html b/docs/namespace_goog_json.html index 7ce0e5b..a8d4125 100644 --- a/docs/namespace_goog_json.html +++ b/docs/namespace_goog_json.html @@ -1,12 +1,10 @@ -goog.json

        Namespace goog.json

        code »

        Classes

        goog.json.Serializer
        Class that is used to serialize JSON objects to a string.
        Show:

        Type Definitions

        code »goog.json.Replacer : function(this: Object, string, *): *
        JSON replacer, as defined in Section 15.12.3 of the ES5 spec. - - TODO(nicksantos): Array should also be a valid replacer.
        code »goog.json.Reviver : function(this: Object, string, *): *
        JSON reviver, as defined in Section 15.12.2 of the ES5 spec.

        Global Functions

        Tests if a string is an invalid JSON string. This only ensures that we are - not using any invalid characters

        Parameters
        s: string
        The string to test.
        Returns
        True if the input is a valid JSON string.

        Parses a JSON string and returns the result. This throws an exception if +goog.json

        Namespace goog.json

        code »

        Classes

        goog.json.Serializer
        Class that is used to serialize JSON objects to a string.
        Show:

        Type Definitions

        code »goog.json.Replacer : function(this: Object, string, *): *
        JSON replacer, as defined in Section 15.12.3 of the ES5 spec.
        code »goog.json.Reviver : function(this: Object, string, *): *
        JSON reviver, as defined in Section 15.12.2 of the ES5 spec.

        Global Functions

        Tests if a string is an invalid JSON string. This only ensures that we are + not using any invalid characters

        Parameters
        s: string
        The string to test.
        Returns
        True if the input is a valid JSON string.

        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.

        Parameters
        s: *
        The JSON string to parse.
        Returns
        The object generated from the JSON string.
        code »goog.json.serialize ( object, opt_replacer )string

        Serializes an object or a value to a JSON string.

        Parameters
        object: *
        The object to serialize.
        opt_replacer: ?goog.json.Replacer=
        A replacer function + the string then you should use unsafeParse instead.
        Parameters
        s: *
        The JSON string to parse.
        Returns
        The object generated from the JSON string, or null.
        Throws
        if s is invalid JSON.
        code »goog.json.serialize ( object, opt_replacer )string

        Serializes an object or a value to a JSON string.

        Parameters
        object: *
        The object to serialize.
        opt_replacer: ?goog.json.Replacer=
        A replacer function called for each (key, value) pair that determines how the value should be serialized. By defult, this just returns the value - and allows default serialization to kick in.
        Returns
        A JSON string representation of the input.
        Throws
        if there are loops in the object graph.

        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.

        Parameters
        s: string
        The JSON string to parse.
        Returns
        The object generated from the JSON string.
        \ No newline at end of file + and allows default serialization to kick in.
        Returns
        A JSON string representation of the input.
        Throws
        if there are loops in the object graph.

        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.

        Parameters
        s: string
        The JSON string to parse.
        Returns
        The object generated from the JSON string.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_labs.html b/docs/namespace_goog_labs.html index 873f175..ec6f76c 100644 --- a/docs/namespace_goog_labs.html +++ b/docs/namespace_goog_labs.html @@ -1 +1 @@ -goog.labs

        Namespace goog.labs

        code »
        Show:
        \ No newline at end of file +goog.labs

        Namespace goog.labs

        code »
        Show:
        \ No newline at end of file diff --git a/docs/namespace_goog_labs_testing.html b/docs/namespace_goog_labs_testing.html index 2d4df99..e97e643 100644 --- a/docs/namespace_goog_labs_testing.html +++ b/docs/namespace_goog_labs_testing.html @@ -1 +1 @@ -goog.labs.testing

        Namespace goog.labs.testing

        code »

        Interfaces

        goog.labs.testing.Matcher
        A matcher object to be used in assertThat statements.

        Classes

        goog.labs.testing.AllOfMatcher
        The AllOf matcher.
        goog.labs.testing.AnyOfMatcher
        The AnyOf matcher.
        goog.labs.testing.CloseToMatcher
        The CloseTo matcher.
        goog.labs.testing.ContainsStringMatcher
        The ContainsString matcher.
        goog.labs.testing.EndsWithMatcher
        The EndsWith matcher.
        goog.labs.testing.EqualToIgnoringWhitespaceMatcher
        The EqualToIgnoringWhitespace matcher.
        goog.labs.testing.EqualToMatcher
        The EqualTo matcher.
        goog.labs.testing.EqualsMatcher
        The Equals matcher.
        goog.labs.testing.GreaterThanEqualToMatcher
        The GreaterThanEqualTo matcher.
        goog.labs.testing.GreaterThanMatcher
        The GreaterThan matcher.
        goog.labs.testing.HasPropertyMatcher
        The HasProperty matcher.
        goog.labs.testing.InstanceOfMatcher
        The InstanceOf matcher.
        goog.labs.testing.IsNotMatcher
        The IsNot matcher.
        goog.labs.testing.IsNullMatcher
        The IsNull matcher.
        goog.labs.testing.IsNullOrUndefinedMatcher
        The IsNullOrUndefined matcher.
        goog.labs.testing.IsUndefinedMatcher
        The IsUndefined matcher.
        goog.labs.testing.LessThanEqualToMatcher
        The LessThanEqualTo matcher.
        goog.labs.testing.LessThanMatcher
        The lessThan matcher.
        goog.labs.testing.MatcherError
        Error thrown when a Matcher fails to match the input value.
        goog.labs.testing.ObjectEqualsMatcher
        The Equals matcher.
        goog.labs.testing.RegexMatcher
        The MatchesRegex matcher.
        goog.labs.testing.StartsWithMatcher
        The StartsWith matcher.
        goog.labs.testing.StringContainsInOrderMatcher
        The StringContainsInOrdermatcher.
        Show:

        Global Functions

        code »goog.labs.testing.assertThat ( actual, matcher, opt_reason )

        Asserts that the actual value evaluated by the matcher is true.

        Parameters
        actual: *
        The object to assert by the matcher.
        matcher: !goog.labs.testing.Matcher
        A matcher to verify values.
        opt_reason: string=
        Description of what is asserted.
        \ No newline at end of file +goog.labs.testing

        Namespace goog.labs.testing

        code »

        Interfaces

        goog.labs.testing.Matcher
        A matcher object to be used in assertThat statements.

        Classes

        goog.labs.testing.AllOfMatcher
        The AllOf matcher.
        goog.labs.testing.AnyOfMatcher
        The AnyOf matcher.
        goog.labs.testing.CloseToMatcher
        The CloseTo matcher.
        goog.labs.testing.ContainsStringMatcher
        The ContainsString matcher.
        goog.labs.testing.EndsWithMatcher
        The EndsWith matcher.
        goog.labs.testing.EqualToIgnoringWhitespaceMatcher
        The EqualToIgnoringWhitespace matcher.
        goog.labs.testing.EqualToMatcher
        The EqualTo matcher.
        goog.labs.testing.EqualsMatcher
        The Equals matcher.
        goog.labs.testing.GreaterThanEqualToMatcher
        The GreaterThanEqualTo matcher.
        goog.labs.testing.GreaterThanMatcher
        The GreaterThan matcher.
        goog.labs.testing.HasPropertyMatcher
        The HasProperty matcher.
        goog.labs.testing.InstanceOfMatcher
        The InstanceOf matcher.
        goog.labs.testing.IsNotMatcher
        The IsNot matcher.
        goog.labs.testing.IsNullMatcher
        The IsNull matcher.
        goog.labs.testing.IsNullOrUndefinedMatcher
        The IsNullOrUndefined matcher.
        goog.labs.testing.IsUndefinedMatcher
        The IsUndefined matcher.
        goog.labs.testing.LessThanEqualToMatcher
        The LessThanEqualTo matcher.
        goog.labs.testing.LessThanMatcher
        The lessThan matcher.
        goog.labs.testing.MatcherError
        Error thrown when a Matcher fails to match the input value.
        goog.labs.testing.ObjectEqualsMatcher
        The Equals matcher.
        goog.labs.testing.RegexMatcher
        The MatchesRegex matcher.
        goog.labs.testing.StartsWithMatcher
        The StartsWith matcher.
        goog.labs.testing.StringContainsInOrderMatcher
        The StringContainsInOrdermatcher.
        Show:

        Global Functions

        code »goog.labs.testing.assertThat ( actual, matcher, opt_reason )

        Asserts that the actual value evaluated by the matcher is true.

        Parameters
        actual: *
        The object to assert by the matcher.
        matcher: !goog.labs.testing.Matcher
        A matcher to verify values.
        opt_reason: string=
        Description of what is asserted.
        \ No newline at end of file diff --git a/docs/namespace_goog_labs_userAgent.html b/docs/namespace_goog_labs_userAgent.html new file mode 100644 index 0000000..732c4b7 --- /dev/null +++ b/docs/namespace_goog_labs_userAgent.html @@ -0,0 +1 @@ +goog.labs.userAgent

        Namespace goog.labs.userAgent

        code »
        Show:
        \ No newline at end of file diff --git a/docs/namespace_goog_labs_userAgent_browser.html b/docs/namespace_goog_labs_userAgent_browser.html new file mode 100644 index 0000000..acc4108 --- /dev/null +++ b/docs/namespace_goog_labs_userAgent_browser.html @@ -0,0 +1,13 @@ +goog.labs.userAgent.browser

        Namespace goog.labs.userAgent.browser

        code »
        Show:

        Global Functions

        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

        Parameters
        userAgent: string
        the User-Agent.
        Returns
        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.)
        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.

        For more information, see: + http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html

        Returns
        Whether the user's browser is Silk.
        Parameters
        version: (string|number)
        The version to check.
        Returns
        Whether the browser version is higher or the same as the + given version.
        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.
        \ No newline at end of file diff --git a/docs/namespace_goog_labs_userAgent_engine.html b/docs/namespace_goog_labs_userAgent_engine.html new file mode 100644 index 0000000..1f9b838 --- /dev/null +++ b/docs/namespace_goog_labs_userAgent_engine.html @@ -0,0 +1,4 @@ +goog.labs.userAgent.engine

        Namespace goog.labs.userAgent.engine

        code »
        Show:

        Global Functions

        Returns
        The rendering engine's version or empty string if version + can't be determined.
        Parameters
        tuples: !Array
        Version tuples.
        key: string
        The key to look for.
        Returns
        The version string of the given key, if present. + Otherwise, the empty string.
        Returns
        Whether the rendering engine is Gecko.
        Returns
        Whether the rendering engine is Presto.
        Returns
        Whether the rendering engine is Trident.
        Parameters
        version: (string|number)
        The version to check.
        Returns
        Whether the rendering engine version is higher or the same + as the given version.
        Returns
        Whether the rendering engine is WebKit.
        \ No newline at end of file diff --git a/docs/namespace_goog_labs_userAgent_util.html b/docs/namespace_goog_labs_userAgent_util.html new file mode 100644 index 0000000..e56994c --- /dev/null +++ b/docs/namespace_goog_labs_userAgent_util.html @@ -0,0 +1,9 @@ +goog.labs.userAgent.util

        Namespace goog.labs.userAgent.util

        code »
        Show:

        Global Functions

        Parses the user agent into tuples for each section.

        Parameters
        userAgent
        Returns
        Tuples of key, version, and the contents + of the parenthetical.

        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.

        Returns
        The user agent string.
        Parameters
        str
        Returns
        Whether the user agent contains the given string, ignoring + case.
        Parameters
        str
        Returns
        Whether the user agent contains the given string.

        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.

        Parameters
        opt_userAgent: ?string=
        The User-Agent override.

        Global Properties

        A possible override for applications which wish to not check + navigator.userAgent but use a specified value for detection instead.

        \ No newline at end of file diff --git a/docs/namespace_goog_math.html b/docs/namespace_goog_math.html new file mode 100644 index 0000000..224a4e6 --- /dev/null +++ b/docs/namespace_goog_math.html @@ -0,0 +1,61 @@ +goog.math

        Namespace goog.math

        code »

        Classes

        goog.math.Box
        Class for representing a box.
        goog.math.Coordinate
        Class for representing coordinates and positions.
        goog.math.Rect
        Class for representing rectangular regions.
        goog.math.Size
        Class for representing sizes consisting of a width and height.
        Show:

        Global Functions

        code »goog.math.angle ( x1, y1, x2, y2 )number

        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.

        Parameters
        x1: number
        x of first point.
        y1: number
        y of first point.
        x2: number
        x of second point.
        y2: number
        y of second point.
        Returns
        Standardized angle in degrees of the vector from + x1,y1 to x2,y2.
        code »goog.math.angleDifference ( startAngle, endAngle )number

        Computes the difference between startAngle and endAngle (angles in degrees).

        Parameters
        startAngle: number
        Start angle in degrees.
        endAngle: number
        End angle in degrees.
        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.
        code »goog.math.angleDx ( degrees, radius )number

        For a given angle and radius, finds the X portion of the offset.

        Parameters
        degrees: number
        Angle in degrees (zero points in +X direction).
        radius: number
        Radius.
        Returns
        The x-distance for the angle and radius.
        code »goog.math.angleDy ( degrees, radius )number

        For a given angle and radius, finds the Y portion of the offset.

        Parameters
        degrees: number
        Angle in degrees (zero points in +X direction).
        radius: number
        Radius.
        Returns
        The y-distance for the angle and radius.

        Returns the arithmetic mean of the arguments.

        Parameters
        var_args: ...number
        Numbers to average.
        Returns
        The average of the arguments (NaN if no arguments + were provided or any of the arguments is not a valid number).
        code »goog.math.clamp ( value, min, max )number

        Takes a number and clamps it to within the provided bounds.

        Parameters
        value: number
        The input number.
        min: number
        The minimum value to return.
        max: number
        The maximum value to return.
        Returns
        The input number if it is within bounds, or the nearest + number within the bounds.

        Returns whether the supplied number is finite and not NaN.

        Parameters
        num: number
        The number to test.
        Returns
        Whether num is a finite number.

        Returns whether the supplied number represents an integer, i.e. that is has + no fractional component. No range-checking is performed on the number.

        Parameters
        num: number
        The number to test.
        Returns
        Whether num is an integer.

        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).

        Parameters
        a: number
        A number.
        b: number
        A number.
        x: number
        The proportion between a and b.
        Returns
        The interpolated value between a and b.

        Returns the precise value of floor(log10(num)). + Simpler implementations didn't work because of floating point rounding + errors. For example +

          +
        • Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3. +
        • Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15. +
        • Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1. +
        Parameters
        num: number
        A floating point number.
        Returns
        Its logarithm to base 10 rounded down to the nearest + integer if num > 0. -Infinity if num == 0. NaN if num < 0.
        code »goog.math.longestCommonSubsequence ( array1, array2, opt_compareFn, opt_collectorFn )!Array.<Object>

        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.

        Parameters
        array1: Array.<Object>
        First array of objects.
        array2: Array.<Object>
        Second array of objects.
        opt_compareFn: Function=
        Function that acts as a custom comparator + for the array ojects. Function should return true if objects are equal, + otherwise false.
        opt_collectorFn: Function=
        Function used to decide what to return + as a result subsequence. It accepts 2 arguments: index of common element + in the first array and index in the second. The default function returns + element from the first array.
        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.

        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.

        Parameters
        a: number
        The dividend.
        b: number
        The divisor.
        Returns
        a % b where the result is between 0 and b (either 0 <= x < b + or b < x <= 0, depending on the sign of b).
        code »goog.math.nearlyEquals ( a, b, opt_tolerance )boolean

        Tests whether the two values are equal to each other, within a certain + tolerance to adjust for floating point errors.

        Parameters
        a: number
        A number.
        b: number
        A number.
        opt_tolerance: number=
        Optional tolerance range. Defaults + to 0.000001. If specified, should be greater than 0.
        Returns
        Whether a and b are nearly equal.

        Returns a random integer greater than or equal to 0 and less than a.

        Parameters
        a: number
        The upper bound for the random integer (exclusive).
        Returns
        A random integer N such that 0 <= N < a.
        code »goog.math.safeCeil ( num, opt_epsilon )number

        A tweaked variant of Math.ceil. See goog.math.safeFloor for + details.

        Parameters
        num: number
        A number.
        opt_epsilon: number=
        An infinitesimally small positive number, the + rounding error to tolerate.
        Returns
        The smallest integer greater than or equal to num.
        code »goog.math.safeFloor ( num, opt_epsilon )number

        A tweaked variant of Math.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.

        Parameters
        num: number
        A number.
        opt_epsilon: number=
        An infinitesimally small positive number, the + rounding error to tolerate.
        Returns
        The largest integer less than or equal to num.

        Returns the unbiased sample variance of the arguments. For a definition, + see e.g. http://en.wikipedia.org/wiki/Variance

        Parameters
        var_args: ...number
        Number samples to analyze.
        Returns
        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).

        Returns the sign of a number as per the "sign" or "signum" function.

        Parameters
        x: number
        The number to take the sign of.
        Returns
        -1 when negative, 1 when positive, 0 when 0.

        Normalizes an angle to be in range [0-360). Angles outside this range will + be normalized to be the equivalent angle with that range.

        Parameters
        angle: number
        Angle in degrees.
        Returns
        Standardized angle.

        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.

        Parameters
        angle: number
        Angle in radians.
        Returns
        Standardized angle.

        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

        Parameters
        var_args: ...number
        Number samples to analyze.
        Returns
        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).
        code »goog.math.sum ( var_args )number

        Returns the sum of the arguments.

        Parameters
        var_args: ...number
        Numbers to add.
        Returns
        The sum of the arguments (0 if no arguments were provided, + NaN if any of the arguments is not a valid number).
        code »goog.math.toDegrees ( angleRadians )number

        Converts radians to degrees.

        Parameters
        angleRadians: number
        Angle in radians.
        Returns
        Angle in degrees.
        code »goog.math.toRadians ( angleDegrees )number

        Converts degrees to radians.

        Parameters
        angleDegrees: number
        Angle in degrees.
        Returns
        Angle in radians.

        Returns a random number greater than or equal to a and less than + b.

        Parameters
        a: number
        The lower bound for the random number (inclusive).
        b: number
        The upper bound for the random number (exclusive).
        Returns
        A random number N such that a <= N < b.
        \ No newline at end of file diff --git a/docs/namespace_goog_net.html b/docs/namespace_goog_net.html index 0cc40e8..8079e41 100644 --- a/docs/namespace_goog_net.html +++ b/docs/namespace_goog_net.html @@ -1 +1 @@ -goog.net

        Namespace goog.net

        code »

        Classes

        goog.net.DefaultXmlHttpFactory
        Default factory to use when creating xhr objects.
        goog.net.WrapperXmlHttpFactory
        An xhr factory subclass which can be constructed using two factory methods.
        goog.net.XmlHttpFactory
        Abstract base class for an XmlHttpRequest factory.
        Show:

        Global Functions

        code »goog.net.XmlHttp ( )!(XMLHttpRequest|GearsHttpRequest)

        Static class for creating XMLHttpRequest objects.

        Returns
        A new XMLHttpRequest object.
        \ No newline at end of file +goog.net

        Namespace goog.net

        code »

        Interfaces

        goog.net.XhrLike
        Interface for the common parts of XMLHttpRequest.

        Classes

        goog.net.DefaultXmlHttpFactory
        Default factory to use when creating xhr objects.
        goog.net.WrapperXmlHttpFactory
        An xhr factory subclass which can be constructed using two factory methods.
        goog.net.XmlHttpFactory
        Abstract base class for an XmlHttpRequest factory.
        Show:

        Global Functions

        Static class for creating XMLHttpRequest objects.

        Returns
        A new XMLHttpRequest object.
        \ No newline at end of file diff --git a/docs/namespace_goog_net_XmlHttp.html b/docs/namespace_goog_net_XmlHttp.html index d9cce79..6d28fc5 100644 --- a/docs/namespace_goog_net_XmlHttp.html +++ b/docs/namespace_goog_net_XmlHttp.html @@ -1,4 +1,4 @@ -goog.net.XmlHttp

        Namespace goog.net.XmlHttp

        code »

        Static class for creating XMLHttpRequest objects.

        Main

        XmlHttp ( )!(XMLHttpRequest|GearsHttpRequest)
        Returns
        A new XMLHttpRequest object.

        Enumerations

        goog.net.XmlHttp.OptionType
        Type of options that an XmlHttp object can have.
        goog.net.XmlHttp.ReadyState
        Status constants for XMLHTTP, matches: +goog.net.XmlHttp

        Namespace goog.net.XmlHttp

        code »

        Static class for creating XMLHttpRequest objects.

        Main

        XmlHttp ( )!goog.net.XhrLike.OrNative
        Returns
        A new XMLHttpRequest object.

        Enumerations

        goog.net.XmlHttp.OptionType
        Type of options that an XmlHttp object can have.
        goog.net.XmlHttp.ReadyState
        Status constants for XMLHTTP, matches: http://msdn.microsoft.com/library/default.asp?url=/library/ - en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp
        Show:

        Global Functions

        Gets the options to use with the XMLHttpRequest objects obtained using - the static methods.

        Returns
        The options.
        code »goog.net.XmlHttp.setFactory ( factory, optionsFactory )
        Deprecated: Use setGlobalFactory instead.

        Sets the factories for creating XMLHttpRequest objects and their options.

        Parameters
        factory: Function
        The factory for XMLHttpRequest objects.
        optionsFactory: Function
        The factory for options.

        Sets the global factory object.

        Parameters
        factory: !goog.net.XmlHttpFactory
        New global factory object.

        Global Properties

        The global factory instance for creating XMLHttpRequest objects.

        Compiler Constants

        \ No newline at end of file + en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp
        Show:

        Global Functions

        Gets the options to use with the XMLHttpRequest objects obtained using + the static methods.

        Returns
        The options.
        code »goog.net.XmlHttp.setFactory ( factory, optionsFactory )
        Deprecated: Use setGlobalFactory instead.

        Sets the factories for creating XMLHttpRequest objects and their options.

        Parameters
        factory: Function
        The factory for XMLHttpRequest objects.
        optionsFactory: Function
        The factory for options.

        Sets the global factory object.

        Parameters
        factory: !goog.net.XmlHttpFactory
        New global factory object.

        Global Properties

        The global factory instance for creating XMLHttpRequest objects.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_net_XmlHttpDefines.html b/docs/namespace_goog_net_XmlHttpDefines.html new file mode 100644 index 0000000..18c999c --- /dev/null +++ b/docs/namespace_goog_net_XmlHttpDefines.html @@ -0,0 +1 @@ +goog.net.XmlHttpDefines

        Namespace goog.net.XmlHttpDefines

        code »
        Show:

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_object.html b/docs/namespace_goog_object.html index 8bf0a43..c9af760 100644 --- a/docs/namespace_goog_object.html +++ b/docs/namespace_goog_object.html @@ -1,70 +1,72 @@ -goog.object

        Namespace goog.object

        code »
        Show:

        Global Functions

        code »<K, V> goog.object.add ( obj, key, val )

        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.

        Parameters
        obj: Object.<K, V>
        The object to which to add the key-value pair.
        key: string
        The key to add.
        val: V
        The value to add.

        Removes all key value pairs from the object/map/hash.

        Parameters
        obj: Object
        The object to clear.
        code »<K, V> goog.object.clone ( obj )!Object.<K, V>

        Does a flat clone of the object.

        Parameters
        obj: Object.<K, V>
        Object to clone.
        Returns
        Clone of the input object.
        code »<K, V> goog.object.contains ( obj, val )boolean

        Whether the object/hash/map contains the given object as a value. - An alias for goog.object.containsValue(obj, val).

        Parameters
        obj: Object.<K, V>
        The object in which to look for val.
        val: V
        The object for which to check.
        Returns
        true if val is present.

        Whether the object/map/hash contains the given key.

        Parameters
        obj: Object
        The object in which to look for key.
        key: *
        The key for which to check.
        Returns
        true If the map contains the key.

        Whether the object/map/hash contains the given value. This is O(n).

        Parameters
        obj: Object.<K, V>
        The object in which to look for val.
        val: V
        The value for which to check.
        Returns
        true If the map contains the value.

        Creates a new object built from the key-value pairs provided as arguments.

        Parameters
        var_args: ...*
        If only one argument is provided and it is an array +goog.object

        Namespace goog.object

        code »
        Show:

        Global Functions

        code »<K, V> goog.object.add ( obj, key, val )

        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.

        Parameters
        obj: Object.<K, V>
        The object to which to add the key-value pair.
        key: string
        The key to add.
        val: V
        The value to add.

        Removes all key value pairs from the object/map/hash.

        Parameters
        obj: Object
        The object to clear.
        code »<K, V> goog.object.clone ( obj )!Object.<K, V>

        Does a flat clone of the object.

        Parameters
        obj: Object.<K, V>
        Object to clone.
        Returns
        Clone of the input object.
        code »<K, V> goog.object.contains ( obj, val )boolean

        Whether the object/hash/map contains the given object as a value. + An alias for goog.object.containsValue(obj, val).

        Parameters
        obj: Object.<K, V>
        The object in which to look for val.
        val: V
        The object for which to check.
        Returns
        true if val is present.

        Whether the object/map/hash contains the given key.

        Parameters
        obj: Object
        The object in which to look for key.
        key: *
        The key for which to check.
        Returns
        true If the map contains the key.

        Whether the object/map/hash contains the given value. This is O(n).

        Parameters
        obj: Object.<K, V>
        The object in which to look for val.
        val: V
        The value for which to check.
        Returns
        true If the map contains the value.

        Creates a new object built from the key-value pairs provided as arguments.

        Parameters
        var_args: ...*
        If only one argument is provided and it is an array then this is used as the arguments, otherwise even arguments are used as the property names and odd arguments are used as the property values.
        Returns
        The new object.
        Throws
        Error
        If there are uneven number of arguments or there is only one - non array argument.

        Creates an immutable view of the underlying object, if the browser + non array argument.

        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.

        Parameters
        obj: !Object.<K, V>
        An object.
        Returns
        An immutable view of that object, or the - original object if this browser does not support immutables.

        Creates a new object where the property names come from the arguments but + original object if this browser does not support immutables.

        Creates a new object where the property names come from the arguments but the value is always set to true

        Parameters
        var_args: ...*
        If only one argument is provided and it is an array then this is used as the arguments, otherwise the arguments are used - as the property names.
        Returns
        The new object.
        code »<T, K, V> goog.object.every ( obj, f, opt_obj )boolean

        Calls a function for each element in an object/map/hash. If + as the property names.Returns

        The new object.

        Compares two objects for equality using === on the values.

        Parameters
        a
        b
        code »<T, K, V> goog.object.every ( obj, f, opt_obj )boolean

        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.

        Parameters
        obj: Object.<K, V>
        The object to check.
        f: ?function(this: T, V, ?, Object.<K, V>): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the object) and should - return a boolean.
        opt_obj: T=
        This is used as the 'this' object within f.
        Returns
        false if any element fails the test.
        code »goog.object.extend ( target, var_args )

        Extends an object with another object. + return a boolean.

        opt_obj: T=
        This is used as the 'this' object within f.Returns
        false if any element fails the test.
        code »goog.object.extend ( target, var_args )

        Extends an object with another object. This operates 'in-place'; it does not create a new Object. Example: var o = {}; goog.object.extend(o, {a: 0, b: 1}); o; // {a: 0, b: 1} - goog.object.extend(o, {c: 2}); - o; // {a: 0, b: 1, c: 2}

        Parameters
        target: Object
        The object to modify.
        var_args: ...Object
        The objects from which values will be copied.
        code »<T, K, V> goog.object.filter ( obj, f, opt_obj )!Object.<K, V>

        Calls a function for each element in an object/map/hash. If that call returns + goog.object.extend(o, {b: 2, c: 3}); + o; // {a: 0, b: 2, c: 3}

        Parameters
        target: Object
        The object to modify. Existing properties will be + overwritten if they are also present in one of the objects in + var_args.
        var_args: ...Object
        The objects from which values will be copied.
        code »<T, K, V> goog.object.filter ( obj, f, opt_obj )!Object.<K, V>

        Calls a function for each element in an object/map/hash. If that call returns true, adds the element to a new object.

        Parameters
        obj: Object.<K, V>
        The object over which to iterate.
        f: function(this: T, V, ?, Object.<K, V>): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the object) and should return a boolean. If the return value is true the element is added to the result object. If it is false the element is not included.
        opt_obj: T=
        This is used as the 'this' object within f.
        Returns
        a new object in which only elements that passed the - test are present.
        code »<T, K, V> goog.object.findKey ( obj, f, opt_this )(string|undefined)

        Searches an object for an element that satisfies the given condition and + test are present.

        code »<T, K, V> goog.object.findKey ( obj, f, opt_this )(string|undefined)

        Searches an object for an element that satisfies the given condition and returns its key.

        Parameters
        obj: Object.<K, V>
        The object to search in.
        f: function(this: T, V, string, Object.<K, V>): boolean
        The function to call for every element. Takes 3 arguments (the value, the key and the object) and should return a boolean.
        opt_this: T=
        An optional "this" context for the function.
        Returns
        The key of an element for which the function - returns true or undefined if no such element is found.
        code »<T, K, V> goog.object.findValue ( obj, f, opt_this )V

        Searches an object for an element that satisfies the given condition and + returns true or undefined if no such element is found.

        code »<T, K, V> goog.object.findValue ( obj, f, opt_this )V

        Searches an object for an element that satisfies the given condition and returns its value.

        Parameters
        obj: Object.<K, V>
        The object to search in.
        f: function(this: T, V, string, Object.<K, V>): boolean
        The function to call for every element. Takes 3 arguments (the value, the key and the object) and should return a boolean.
        opt_this: T=
        An optional "this" context for the function.
        Returns
        The value of an element for which the function returns true or - undefined if no such element is found.
        code »<T, K, V> goog.object.forEach ( obj, f, opt_obj )

        Calls a function for each element in an object/map/hash.

        Parameters
        obj: Object.<K, V>
        The object over which to iterate.
        f: function(this: T, V, ?, Object.<K, V>): ?
        The function to call + undefined if no such element is found.
        code »<T, K, V> goog.object.forEach ( obj, f, opt_obj )

        Calls a function for each element in an object/map/hash.

        Parameters
        obj: Object.<K, V>
        The object over which to iterate.
        f: function(this: T, V, ?, Object.<K, V>): ?
        The function to call for every element. This function takes 3 arguments (the element, the - index and the object) and the return value is ignored.
        opt_obj: T=
        This is used as the 'this' object within f.
        code »<K, V, R> goog.object.get ( obj, key, opt_val )(V|R|undefined)

        Returns the value for the given key.

        Parameters
        obj: Object.<K, V>
        The object from which to get the value.
        key: string
        The key for which to get the value.
        opt_val: R=
        The value to return if no item is found for the given - key (default is undefined).
        Returns
        The value for the given key.

        Returns one key from the object map, if any exists. + index and the object) and the return value is ignored.

        opt_obj: T=
        This is used as the 'this' object within f.
        code »<K, V, R> goog.object.get ( obj, key, opt_val )(V|R|undefined)

        Returns the value for the given key.

        Parameters
        obj: Object.<K, V>
        The object from which to get the value.
        key: string
        The key for which to get the value.
        opt_val: R=
        The value to return if no item is found for the given + key (default is undefined).
        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).

        Parameters
        obj: Object
        The object to pick a key from.
        Returns
        The key or undefined if the object is empty.

        Returns one value from the object map, if any exists. + browsers (a know exception is Konqueror).

        Parameters
        obj: Object
        The object to pick a key from.
        Returns
        The key or undefined if the object is empty.

        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).

        Parameters
        obj: Object.<K, V>
        The object to pick a value from.
        Returns
        The value or undefined if the object is empty.

        Returns the number of key-value pairs in the object map.

        Parameters
        obj: Object
        The object for which to get the number of key-value - pairs.
        Returns
        The number of key-value pairs in the object map.

        Returns the keys of the object/map/hash.

        Parameters
        obj: Object
        The object from which to get the keys.
        Returns
        Array of property keys.
        code »goog.object.getValueByKeys ( obj, var_args )*

        Get a value from an object multiple levels deep. This is useful for + browsers (a know exception is Konqueror).

        Parameters
        obj: Object.<K, V>
        The object to pick a value from.
        Returns
        The value or undefined if the object is empty.

        Returns the number of key-value pairs in the object map.

        Parameters
        obj: Object
        The object for which to get the number of key-value + pairs.
        Returns
        The number of key-value pairs in the object map.

        Returns the keys of the object/map/hash.

        Parameters
        obj: Object
        The object from which to get the keys.
        Returns
        Array of property keys.
        code »goog.object.getValueByKeys ( obj, var_args )*

        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)

        Parameters
        obj: !Object
        An object to get the value from. Can be array-like.
        var_args: ...(string|number|!Array)
        A number of keys (as strings, or numbers, for array-like objects). Can also be specified as a single array of keys.
        Returns
        The resulting value. If, at any point, the value for a key - is undefined, returns undefined.
        code »<K, V> goog.object.getValues ( obj )!Array.<V>

        Returns the values of the object/map/hash.

        Parameters
        obj: Object.<K, V>
        The object from which to get the values.
        Returns
        The values in the object/map/hash.

        Whether the object/map/hash is empty.

        Parameters
        obj: Object
        The object to test.
        Returns
        true if obj is empty.
        Parameters
        obj: !Object
        An object.
        Returns
        Whether this is an immutable view of the object.
        code »<T, K, V, R> goog.object.map ( obj, f, opt_obj )!Object.<K, R>

        For every element in an object/map/hash calls a function and inserts the + is undefined, returns undefined.

        code »<K, V> goog.object.getValues ( obj )!Array.<V>

        Returns the values of the object/map/hash.

        Parameters
        obj: Object.<K, V>
        The object from which to get the values.
        Returns
        The values in the object/map/hash.

        Whether the object/map/hash is empty.

        Parameters
        obj: Object
        The object to test.
        Returns
        true if obj is empty.
        Parameters
        obj: !Object
        An object.
        Returns
        Whether this is an immutable view of the object.
        code »<T, K, V, R> goog.object.map ( obj, f, opt_obj )!Object.<K, R>

        For every element in an object/map/hash calls a function and inserts the result into a new object.

        Parameters
        obj: Object.<K, V>
        The object over which to iterate.
        f: function(this: T, V, ?, Object.<K, V>): R
        The function to call for every element. This function takes 3 arguments (the element, the index and the object) and should return something. The result will be inserted - into a new object.
        opt_obj: T=
        This is used as the 'this' object within f.
        Returns
        a new object with the results from f.

        Removes a key-value pair based on the key.

        Parameters
        obj: Object
        The object from which to remove the key.
        key: *
        The key to remove.
        Returns
        Whether an element was removed.
        code »<K, V> goog.object.set ( obj, key, value )

        Adds a key-value pair to the object/map/hash.

        Parameters
        obj: Object.<K, V>
        The object to which to add the key-value pair.
        key: string
        The key to add.
        value: V
        The value to add.
        code »<K, V> goog.object.setIfUndefined ( obj, key, value )V

        Adds a key-value pair to the object/map/hash if it doesn't exist yet.

        Parameters
        obj: Object.<K, V>
        The object to which to add the key-value pair.
        key: string
        The key to add.
        value: V
        The value to add if the key wasn't present.
        Returns
        The value of the entry at the end of the function.
        code »<T, K, V> goog.object.some ( obj, f, opt_obj )boolean

        Calls a function for each element in an object/map/hash. If any + into a new object.

        opt_obj: T=
        This is used as the 'this' object within f.Returns
        a new object with the results from f.

        Removes a key-value pair based on the key.

        Parameters
        obj: Object
        The object from which to remove the key.
        key: *
        The key to remove.
        Returns
        Whether an element was removed.
        code »<K, V> goog.object.set ( obj, key, value )

        Adds a key-value pair to the object/map/hash.

        Parameters
        obj: Object.<K, V>
        The object to which to add the key-value pair.
        key: string
        The key to add.
        value: V
        The value to add.
        code »<K, V> goog.object.setIfUndefined ( obj, key, value )V

        Adds a key-value pair to the object/map/hash if it doesn't exist yet.

        Parameters
        obj: Object.<K, V>
        The object to which to add the key-value pair.
        key: string
        The key to add.
        value: V
        The value to add if the key wasn't present.
        Returns
        The value of the entry at the end of the function.
        code »<T, K, V> goog.object.some ( obj, f, opt_obj )boolean

        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.

        Parameters
        obj: Object.<K, V>
        The object to check.
        f: function(this: T, V, ?, Object.<K, V>): boolean
        The function to call for every element. This function takes 3 arguments (the element, the index and the object) and should - return a boolean.
        opt_obj: T=
        This is used as the 'this' object within f.
        Returns
        true if any element passes the test.

        Returns a new object in which all the keys and values are interchanged + return a boolean.

        opt_obj: T=
        This is used as the 'this' object within f.Returns
        true if any element passes the test.

        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.

        Parameters
        obj: Object
        The object to transpose.
        Returns
        The transposed object.

        Clones a value. The input may be an Object, Array, or basic type. Objects and + same value, the chosen transposed value is implementation-dependent.

        Parameters
        obj: Object
        The object to transpose.
        Returns
        The transposed object.

        Clones a value. The input may be an Object, Array, or basic type. Objects and arrays will be cloned recursively. WARNINGS: @@ -72,4 +74,4 @@ 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
        obj: *
        The value to clone.
        Returns
        A clone of the input value.

        Global Properties

        The names of the fields that are defined on Object.prototype.

        \ No newline at end of file + copies UIDs created by getUid into cloned results.
        Parameters
        obj: *
        The value to clone.
        Returns
        A clone of the input value.

        Global Properties

        The names of the fields that are defined on Object.prototype.

        \ No newline at end of file diff --git a/docs/namespace_goog_reflect.html b/docs/namespace_goog_reflect.html new file mode 100644 index 0000000..68871f5 --- /dev/null +++ b/docs/namespace_goog_reflect.html @@ -0,0 +1,7 @@ +goog.reflect

        Namespace goog.reflect

        code »
        Show:

        Global Functions

        Check if a property can be accessed without throwing an exception.

        Parameters
        obj: Object
        The owner of the property.
        prop: string
        The property name.
        Returns
        Whether the property is accessible. Will also return true + if obj is null.
        code »goog.reflect.object ( type, object )Object

        Syntax for object literal casts.

        Parameters
        type: !Function
        Type to cast to.
        object: Object
        Object literal to cast.
        Returns
        The object literal.

        To assert to the compiler that an operation is needed when it would + otherwise be stripped. For example: + + // Force a layout + goog.reflect.sinkValue(dialog.offsetHeight); +

        \ No newline at end of file diff --git a/docs/namespace_goog_string.html b/docs/namespace_goog_string.html index e4d7474..e4ace34 100644 --- a/docs/namespace_goog_string.html +++ b/docs/namespace_goog_string.html @@ -1,4 +1,4 @@ -goog.string

        Namespace goog.string

        code »

        Enumerations

        goog.string.Unicode
        Common Unicode string characters.
        Show:

        Global Functions

        Concatenates string expressions. This is useful +goog.string

        Namespace goog.string

        code »

        Enumerations

        goog.string.Unicode
        Common Unicode string characters.
        Show:

        Global Functions

        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 @@ -7,39 +7,43 @@

        buildString('a', 'b', 'c', 'd') -> 'abcd'
          buildString(null, undefined) -> ''
          
        Parameters
        var_args: ...*
        A list of strings to concatenate. If not a string, - it will be casted to one.
        Returns
        The concatenation of var_args.

        Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.

        Parameters
        str: string
        The string to in which to canonicalize newlines.
        Returns
        str A copy of {@code} with canonicalized newlines.

        A string comparator that ignores case. + it will be casted to one.Returns

        The concatenation of var_args.

        Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.

        Parameters
        str: string
        The string to in which to canonicalize newlines.
        Returns
        str A copy of {@code} with canonicalized newlines.

        A string comparator that ignores case. -1 = str1 less than str2 0 = str1 equals str2 - 1 = str1 greater than str2

        Parameters
        str1: string
        The string to compare.
        str2: string
        The string to compare str1 to.
        Returns
        The comparator result, as described above.

        Case-insensitive suffix-checker.

        Parameters
        str: string
        The string to check.
        suffix: string
        A string to look for at the end of str.
        Returns
        True if str ends with suffix (ignoring - case).

        Case-insensitive equality checker.

        Parameters
        str1: string
        First string to check.
        str2: string
        Second string to check.
        Returns
        True if str1 and str2 are the same string, - ignoring case.

        Case-insensitive prefix-checker.

        Parameters
        str: string
        The string to check.
        prefix: string
        A string to look for at the end of str.
        Returns
        True if str begins with prefix (ignoring - case).

        Removes the breaking spaces from the left and right of the string and + 1 = str1 greater than str2

        Parameters
        str1: string
        The string to compare.
        str2: string
        The string to compare str1 to.
        Returns
        The comparator result, as described above.

        Determines whether a string contains a substring, ignoring case.

        Parameters
        str: string
        The string to search.
        subString: string
        The substring to search for.
        Returns
        Whether str contains subString.

        Case-insensitive suffix-checker.

        Parameters
        str: string
        The string to check.
        suffix: string
        A string to look for at the end of str.
        Returns
        True if str ends with suffix (ignoring + case).

        Case-insensitive equality checker.

        Parameters
        str1: string
        First string to check.
        str2: string
        Second string to check.
        Returns
        True if str1 and str2 are the same string, + ignoring case.

        Case-insensitive prefix-checker.

        Parameters
        str: string
        The string to check.
        prefix: string
        A string to look for at the end of str.
        Returns
        True if str begins with prefix (ignoring + case).

        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.

        Parameters
        str: string
        A string in which to collapse spaces.
        Returns
        Copy of the string with normalized breaking spaces.

        Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines - and tabs) to a single space, and strips leading and trailing whitespace.

        Parameters
        str: string
        Input string.
        Returns
        A copy of str with collapsed whitespace.

        Compares elements of a version number.

        Parameters
        left: (string|number|boolean)
        An element from a version number.
        right: (string|number|boolean)
        An element from a version number.
        Returns
        1 if left is higher. + The original and the result strings render the same way in HTML.
        Parameters
        str: string
        A string in which to collapse spaces.
        Returns
        Copy of the string with normalized breaking spaces.

        Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines + and tabs) to a single space, and strips leading and trailing whitespace.

        Parameters
        str: string
        Input string.
        Returns
        A copy of str with collapsed whitespace.

        Compares elements of a version number.

        Parameters
        left: (string|number|boolean)
        An element from a version number.
        right: (string|number|boolean)
        An element from a version number.
        Returns
        1 if left is higher. 0 if arguments are equal. - -1 if right is higher.
        code »goog.string.compareVersions ( version1, version2 )number

        Compares two version numbers.

        Parameters
        version1: (string|number)
        Version of first item.
        version2: (string|number)
        Version of second item.
        Returns
        1 if version1 is higher. + -1 if right is higher.
        code »goog.string.compareVersions ( version1, version2 )number

        Compares two version numbers.

        Parameters
        version1: (string|number)
        Version of first item.
        version2: (string|number)
        Version of second item.
        Returns
        1 if version1 is higher. 0 if arguments are equal. - -1 if version2 is higher.

        Checks whether a string contains a given substring.

        Parameters
        s: string
        The string to test.
        ss: string
        The substring to test for.
        Returns
        True if s contains ss.

        Returns the non-overlapping occurrences of ss in s. - If either s or ss evalutes to false, then returns zero.

        Parameters
        s: string
        The string to look in.
        ss: string
        The string to look for.
        Returns
        Number of occurrences of ss in s.

        Generates and returns a string which is unique in the current document. - This is useful, for example, to create unique IDs for DOM elements.

        Returns
        A unique id.

        Fast suffix-checker.

        Parameters
        str: string
        The string to check.
        suffix: string
        A string to look for at the end of str.
        Returns
        True if str ends with suffix.

        Takes a character and returns the escaped string for that character. For - example escapeChar(String.fromCharCode(15)) -> "\\x0E".

        Parameters
        c: string
        The character to escape.
        Returns
        An escaped string representing c.

        Takes a string and returns the escaped string for that character.

        Parameters
        str: string
        The string to escape.
        Returns
        An escaped string representing str.

        Returns a string with at least 64-bits of randomness. + -1 if version2 is higher.

        code »goog.string.contains ( str, subString )boolean

        Determines whether a string contains a substring.

        Parameters
        str: string
        The string to search.
        subString: string
        The substring to search for.
        Returns
        Whether str contains subString.

        Returns the non-overlapping occurrences of ss in s. + If either s or ss evalutes to false, then returns zero.

        Parameters
        s: string
        The string to look in.
        ss: string
        The string to look for.
        Returns
        Number of occurrences of ss in s.

        Generates and returns a string which is unique in the current document. + This is useful, for example, to create unique IDs for DOM elements.

        Returns
        A unique id.

        Fast suffix-checker.

        Parameters
        str: string
        The string to check.
        suffix: string
        A string to look for at the end of str.
        Returns
        True if str ends with suffix.

        Takes a character and returns the escaped string for that character. For + example escapeChar(String.fromCharCode(15)) -> "\\x0E".

        Parameters
        c: string
        The character to escape.
        Returns
        An escaped string representing c.

        Takes a string and returns the escaped string for that character.

        Parameters
        str: string
        The string to escape.
        Returns
        An escaped string representing str.

        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.

        Returns
        A random string, e.g. sn1s7vb4gcic.

        String hash function similar to java.lang.String.hashCode(). + make it shorter.

        Returns
        A random string, e.g. sn1s7vb4gcic.

        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).

        Parameters
        str: string
        A string.
        Returns
        Hash value for str, between 0 (inclusive) and 2^32 - (exclusive). The empty string returns 0.
        code »goog.string.htmlEscape ( str, opt_isLikelyToContainHtmlChars )string

        Escape double quote '"' characters in addition to '&', '<', and '>' so that a - string can be included in an HTML tag attribute value within double quotes. + (exclusive). The empty string returns 0.

        code »goog.string.htmlEscape ( str, opt_isLikelyToContainHtmlChars )string

        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 @@ -66,17 +70,17 @@ application grows the difference between the various methods would increase.

        Parameters
        str: string
        string to be escaped.
        opt_isLikelyToContainHtmlChars: boolean=
        Don't perform a check to see if the character needs replacing - use this option if you expect each of the characters to appear often. Leave false if you expect few html - characters to occur in your strings, such as if you are escaping HTML.
        Returns
        An escaped copy of str.

        Checks if a string contains all letters.

        Parameters
        str: string
        string to check.
        Returns
        True if str consists entirely of letters.

        Checks if a string contains only numbers or letters.

        Parameters
        str: string
        string to check.
        Returns
        True if str is alphanumeric.

        Checks if a string is all breaking whitespace.

        Parameters
        str: string
        The string to check.
        Returns
        Whether the string is all breaking whitespace.

        Checks if a string is empty or contains only whitespaces.

        Parameters
        str: string
        The string to check.
        Returns
        True if str is empty or whitespace only.

        Checks if a string is null, undefined, empty or contains only whitespaces.

        Parameters
        str: *
        The string to check.
        Returns
        True ifstr is null, undefined, empty, or - whitespace only.

        Returns whether the given string is lower camel case (e.g. "isFooBar"). + characters to occur in your strings, such as if you are escaping HTML.Returns

        An escaped copy of str.

        Checks if a string contains all letters.

        Parameters
        str: string
        string to check.
        Returns
        True if str consists entirely of letters.

        Checks if a string contains only numbers or letters.

        Parameters
        str: string
        string to check.
        Returns
        True if str is alphanumeric.

        Checks if a string is all breaking whitespace.

        Parameters
        str: string
        The string to check.
        Returns
        Whether the string is all breaking whitespace.

        Checks if a string is empty or contains only whitespaces.

        Parameters
        str: string
        The string to check.
        Returns
        True if str is empty or whitespace only.

        Checks if a string is null, undefined, empty or contains only whitespaces.

        Parameters
        str: *
        The string to check.
        Returns
        True ifstr is null, undefined, empty, or + whitespace only.

        Returns whether the given string is lower camel case (e.g. "isFooBar"). - Note that this assumes the string is entirely letters.

        Parameters
        str: string
        String to test.
        Returns
        Whether the string is lower camel case.

        Checks if a string contains only numbers.

        Parameters
        str: *
        string to check. If not a string, it will be - casted to one.
        Returns
        True if str is numeric.

        Checks if a character is a space character.

        Parameters
        ch: string
        Character to check.
        Returns
        True if {code ch} is a space.

        Checks if a character is a valid unicode character.

        Parameters
        ch: string
        Character to check.
        Returns
        True if {code ch} 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.

        Parameters
        str: string
        String to test.
        Returns
        Whether the string is lower camel case.

        Checks if a string contains only numbers.

        Parameters
        str: *
        string to check. If not a string, it will be + casted to one.
        Returns
        True if str is numeric.

        Checks if a character is a space character.

        Parameters
        ch: string
        Character to check.
        Returns
        True if {code ch} is a space.

        Checks if a character is a valid unicode character.

        Parameters
        ch: string
        Character to check.
        Returns
        True if {code ch} 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.

        Parameters
        str: string
        String to test.
        Returns
        Whether the string is upper camel case.

        Returns a string representation of the given object, with - null and undefined being returned as the empty string.

        Parameters
        obj: *
        The object to convert.
        Returns
        A string representation of the obj.

        Converts \n to
        s or
        s.

        Parameters
        str: string
        The string in which to convert newlines.
        opt_xml: boolean=
        Whether to use XML compatible tags.
        Returns
        A copy of str with converted newlines.

        Normalizes spaces in a string, replacing all consecutive spaces and tabs + Note that this assumes the string is entirely letters.

        Parameters
        str: string
        String to test.
        Returns
        Whether the string is upper camel case.

        Returns a string representation of the given object, with + null and undefined being returned as the empty string.

        Parameters
        obj: *
        The object to convert.
        Returns
        A string representation of the obj.

        Converts \n to
        s or
        s.

        Parameters
        str: string
        The string in which to convert newlines.
        opt_xml: boolean=
        Whether to use XML compatible tags.
        Returns
        A copy of str with converted newlines.

        Normalizes spaces in a string, replacing all consecutive spaces and tabs with a single space. Replaces non-breaking space with a space.

        Parameters
        str: string
        The string in which to normalize spaces.
        Returns
        A copy of str with all consecutive spaces and tabs - replaced with a single space.

        Normalizes whitespace in a string, replacing all whitespace chars with - a space.

        Parameters
        str: string
        The string in which to normalize whitespace.
        Returns
        A copy of str with all whitespace normalized.

        String comparison function that handles numbers in a way humans might expect. + replaced with a single space.

        Normalizes whitespace in a string, replacing all whitespace chars with + a space.

        Parameters
        str: string
        The string in which to normalize whitespace.
        Returns
        A copy of str with all whitespace normalized.

        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. @@ -85,12 +89,12 @@ 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.

        Parameters
        str1: string
        The string to compare in a numerically sensitive way.
        str2: string
        The string to compare str1 to.
        Returns
        less than 0 if str1 < str2, 0 if str1 == str2, greater than - 0 if str1 > str2.
        code »goog.string.padNumber ( num, length, opt_precision )string

        Pads number to given length and optionally rounds it to a given precision. + 0 if str1 > str2.

        code »goog.string.padNumber ( num, length, opt_precision )string

        Pads number to given length and optionally rounds it to a given precision. For example:

        padNumber(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'
        Parameters
        num: number
        The number to pad.
        length: number
        The desired length.
        opt_precision: number=
        The desired precision.
        Returns
        num as a string with the given options.

        Parse a string in decimal or hexidecimal ('0xFFFF') form. + padNumber(1.25, 0) -> '1.25'

        Parameters
        num: number
        The number to pad.
        length: number
        The desired length.
        opt_precision: number=
        The desired precision.
        Returns
        num as a string with the given options.

        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 @@ -101,14 +105,15 @@ this behavior. ES5 forbids it. This function emulates the ES5 behavior. For more information, see Mozilla JS Reference: http://goo.gl/8RiFj

        Parameters
        value: (string|number|null|undefined)
        The value to be parsed.
        Returns
        The number, parsed. If the string failed to parse, this - will be NaN.

        Encloses a string in double quotes and escapes characters so that the - string is a valid JS string.

        Parameters
        s: string
        The string to quote.
        Returns
        A copy of s surrounded by double quotes.

        Escapes characters in the string that are not safe to use in a RegExp.

        Parameters
        s: *
        The string to escape. If not a string, it will be casted - to one.
        Returns
        A RegExp safe, escaped copy of s.

        Removes the first occurrence of a substring from a string.

        Parameters
        s: string
        The base string from which to remove.
        ss: string
        The string to remove.
        Returns
        A copy of s with ss removed or the full - string if nothing is removed.

        Removes all occurrences of a substring from a string.

        Parameters
        s: string
        The base string from which to remove.
        ss: string
        The string to remove.
        Returns
        A copy of s with ss removed or the full - string if nothing is removed.
        code »goog.string.removeAt ( s, index, stringLength )string

        Removes a substring of a specified length at a specific + will be NaN.

        Preserve spaces that would be otherwise collapsed in HTML by replacing them + with non-breaking space Unicode characters.

        Parameters
        str: string
        The string in which to preserve whitespace.
        Returns
        A copy of str with preserved whitespace.

        Encloses a string in double quotes and escapes characters so that the + string is a valid JS string.

        Parameters
        s: string
        The string to quote.
        Returns
        A copy of s surrounded by double quotes.

        Escapes characters in the string that are not safe to use in a RegExp.

        Parameters
        s: *
        The string to escape. If not a string, it will be casted + to one.
        Returns
        A RegExp safe, escaped copy of s.

        Removes the first occurrence of a substring from a string.

        Parameters
        s: string
        The base string from which to remove.
        ss: string
        The string to remove.
        Returns
        A copy of s with ss removed or the full + string if nothing is removed.

        Removes all occurrences of a substring from a string.

        Parameters
        s: string
        The base string from which to remove.
        ss: string
        The string to remove.
        Returns
        A copy of s with ss removed or the full + string if nothing is removed.
        code »goog.string.removeAt ( s, index, stringLength )string

        Removes a substring of a specified length at a specific index in a string.

        Parameters
        s: string
        The base string from which to remove.
        index: number
        The index at which to remove the substring.
        stringLength: number
        The length of the substring to remove.
        Returns
        A copy of s with the substring removed or the full - string if nothing is removed or the input is invalid.
        code »goog.string.repeat ( string, length )string

        Repeats a string n times.

        Parameters
        string: string
        The string to repeat.
        length: number
        The number of times to repeat.
        Returns
        A string containing length repetitions of - string.
        code »goog.string.splitLimit ( str, separator, limit )!Array.<string>

        Splits a string on a separator a limited number of times. + string if nothing is removed or the input is invalid.

        code »goog.string.repeat ( string, length )string

        Repeats a string n times.

        Parameters
        string: string
        The string to repeat.
        length: number
        The number of times to repeat.
        Returns
        A string containing length repetitions of + string.
        code »goog.string.splitLimit ( str, separator, limit )!Array.<string>

        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 @@ -118,8 +123,8 @@ See JavaDoc: http://goo.gl/F2AsY See Mozilla reference: http://goo.gl/dZdZs

        Parameters
        str: string
        String to split.
        separator: string
        The separator.
        limit: number
        The limit to the number of splits. The resulting array will have a maximum length of limit+1. Negative numbers are the same - as zero.
        Returns
        The string, split.

        Fast prefix-checker.

        Parameters
        str: string
        The string to check.
        prefix: string
        A string to look for at the start of str.
        Returns
        True if str begins with prefix.

        Takes a string and replaces newlines with a space. Multiple lines are - replaced with a single space.

        Parameters
        str: string
        The string from which to strip newlines.
        Returns
        A copy of str stripped of newlines.
        code »goog.string.stripQuotes ( str, quoteChars )string

        Strip quote characters around a string. The second argument is a string of + as zero.Returns

        The string, split.

        Fast prefix-checker.

        Parameters
        str: string
        The string to check.
        prefix: string
        A string to look for at the start of str.
        Returns
        True if str begins with prefix.

        Takes a string and replaces newlines with a space. Multiple lines are + replaced with a single space.

        Parameters
        str: string
        The string from which to strip newlines.
        Returns
        A copy of str stripped of newlines.
        code »goog.string.stripQuotes ( str, quoteChars )string

        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: @@ -127,20 +132,18 @@

          goog.string.stripQuotes('"abc"', '"`') --> 'abc'
          goog.string.stripQuotes('`abc`', '"`') --> 'abc'
        - 
        Parameters
        str: string
        The string to strip.
        quoteChars: string
        The quote characters to strip.
        Returns
        A copy of str without the quotes.
        code »goog.string.subs ( str, var_args )string

        Does simple python-style string substitution. +

        Parameters
        str: string
        The string to strip.
        quoteChars: string
        The quote characters to strip.
        Returns
        A copy of str without the quotes.
        code »goog.string.subs ( str, var_args )string

        Does simple python-style string substitution. subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".

        Parameters
        str: string
        The string containing the pattern.
        var_args: ...*
        The items to substitute into the pattern.
        Returns
        A copy of str in which each occurrence of - %s has been replaced an argument from var_args.

        Converts a string from selector-case to camelCase (e.g. from + %s has been replaced an argument from var_args.

        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.

        Parameters
        str: string
        The string in selector-case form.
        Returns
        The string in camelCase form.

        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.

        Parameters
        s: string
        The string to build the map from.
        Returns
        The map of characters used.

        Converts the supplied string to a number, which may be Ininity or NaN. + CSS selectors and HTML dataset keys to their equivalent JS properties.

        Parameters
        str: string
        The string in selector-case form.
        Returns
        The string in camelCase form.

        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)

        Parameters
        str: string
        The string to convert.
        Returns
        The number the supplied string represents, or NaN.

        Converts a string from camelCase to selector-case (e.g. from + (Number(' ') === 0) and (parseFloat('123a') === 123)

        Parameters
        str: string
        The string to convert.
        Returns
        The number the supplied string represents, or NaN.

        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.

        Parameters
        str: string
        The string in camelCase form.
        Returns
        The string in selector-case form.
        code »goog.string.toTitleCase ( str, opt_delimiters )string

        Converts a string into TitleCase. First character of the string is always + style and dataset properties to equivalent CSS selectors and HTML keys.

        Parameters
        str: string
        The string in camelCase form.
        Returns
        The string in selector-case form.
        code »goog.string.toTitleCase ( str, opt_delimiters )string

        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 @@ -164,18 +167,20 @@ goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'

        Parameters
        str: string
        String value in camelCase form.
        opt_delimiters: string=
        Custom delimiter character set used to distinguish words in the string value. Each character represents a single delimiter. When provided, default whitespace delimiter is - overridden and must be explicitly included if needed.
        Returns
        String value in TitleCase form.

        Trims white spaces to the left and right of a string.

        Parameters
        str: string
        The string to trim.
        Returns
        A trimmed copy of str.

        Trims whitespaces at the left end of a string.

        Parameters
        str: string
        The string to left trim.
        Returns
        A trimmed copy of str.

        Trims whitespaces at the right end of a string.

        Parameters
        str: string
        The string to right trim.
        Returns
        A trimmed copy of str.
        code »goog.string.truncate ( str, chars, opt_protectEscapedCharacters )string

        Truncates a string to a certain length and adds '...' if necessary. The + overridden and must be explicitly included if needed.Returns

        String value in TitleCase form.

        Trims white spaces to the left and right of a string.

        Parameters
        str: string
        The string to trim.
        Returns
        A trimmed copy of str.

        Trims whitespaces at the left end of a string.

        Parameters
        str: string
        The string to left trim.
        Returns
        A trimmed copy of str.

        Trims whitespaces at the right end of a string.

        Parameters
        str: string
        The string to right trim.
        Returns
        A trimmed copy of str.
        code »goog.string.truncate ( str, chars, opt_protectEscapedCharacters )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...'.

        Parameters
        str: string
        The string to truncate.
        chars: number
        Max number of characters.
        opt_protectEscapedCharacters: boolean=
        Whether to protect escaped - characters from being cut off in the middle.
        Returns
        The truncated str string.
        code »goog.string.truncateMiddle ( str, chars, opt_protectEscapedCharacters, opt_trailingChars )string

        Truncate a string in the middle, adding "..." if necessary, + characters from being cut off in the middle.Returns

        The truncated str string.
        code »goog.string.truncateMiddle ( str, chars, opt_protectEscapedCharacters, opt_trailingChars )string

        Truncate a string in the middle, adding "..." if necessary, and favoring the beginning of the string.

        Parameters
        str: string
        The string to truncate the middle of.
        chars: number
        Max number of characters.
        opt_protectEscapedCharacters: boolean=
        Whether to protect escaped characters from being cutoff in the middle.
        opt_trailingChars: number=
        Optional number of trailing characters to leave at the end of the string, instead of truncating as close to the - middle as possible.
        Returns
        A truncated copy of str.

        Unescapes an HTML string.

        Parameters
        str: string
        The string to unescape.
        Returns
        An unescaped copy of str.

        Unescapes an HTML string using a DOM to resolve non-XML, non-numeric - entities. This function is XSS-safe and whitespace-preserving.

        Parameters
        str: string
        The string to unescape.
        Returns
        The unescaped str string.

        Unescapes XML entities.

        Parameters
        str: string
        The string to unescape.
        Returns
        An unescaped copy of str.

        URL-decodes the string. We need to specially handle '+'s because - the javascript library doesn't convert them to spaces.

        Parameters
        str: string
        The string to url decode.
        Returns
        The decoded str.

        URL-encodes a string

        Parameters
        str: *
        The string to url-encode.
        Returns
        An encoded copy of str that is safe for urls. + middle as possible.
        Returns
        A truncated copy of str.

        Unescapes an HTML string.

        Parameters
        str: string
        The string to unescape.
        Returns
        An unescaped copy of str.

        Unescapes an HTML string using a DOM to resolve non-XML, non-numeric + entities. This function is XSS-safe and whitespace-preserving.

        Parameters
        str: string
        The string to unescape.
        opt_document: Document=
        An optional document to use for creating + elements. If this is not specified then the default window.document + will be used.
        Returns
        The unescaped str string.

        Unescapes a HTML string using the provided document.

        Parameters
        str: string
        The string to unescape.
        document: !Document
        A document to use in escaping the string.
        Returns
        An unescaped copy of str.

        Unescapes XML entities.

        Parameters
        str: string
        The string to unescape.
        Returns
        An unescaped copy of str.

        URL-decodes the string. We need to specially handle '+'s because + the javascript library doesn't convert them to spaces.

        Parameters
        str: string
        The string to url decode.
        Returns
        The decoded str.

        URL-encodes a string

        Parameters
        str: *
        The string to url-encode.
        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.

        Do escaping of whitespace to preserve spatial formatting. We use character - entity #160 to make it safer for xml.

        Parameters
        str: string
        The string in which to escape whitespace.
        opt_xml: boolean=
        Whether to use XML compatible tags.
        Returns
        An escaped copy of str.

        Global Properties

        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 any character that needs to be escaped.

        Regular expression that matches an ampersand, for use in escaping.

        Regular expression that matches a greater than sign, for use in escaping.

        Character mappings used internally for goog.string.escapeChar.

        Regular expression that matches a less than sign, for use in escaping.

        Regular expression used for splitting a string into substrings of fractional - numbers, integers, and non-numeric characters.

        Regular expression that matches a double quote, for use in escaping.

        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.

        \ No newline at end of file + of URLs *will* be encoded.

        Do escaping of whitespace to preserve spatial formatting. We use character + entity #160 to make it safer for xml.

        Parameters
        str: string
        The string in which to escape whitespace.
        opt_xml: boolean=
        Whether to use XML compatible tags.
        Returns
        An escaped copy of str.

        Global Properties

        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.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_structs.html b/docs/namespace_goog_structs.html index 95e655e..3b83e2b 100644 --- a/docs/namespace_goog_structs.html +++ b/docs/namespace_goog_structs.html @@ -1,11 +1,11 @@ -goog.structs

        Namespace goog.structs

        code »

        Classes

        goog.structs.Map
        Class for Hash Map datastructure.
        Show:

        Global Functions

        Removes all the elements from the collection.

        Parameters
        col: Object
        The collection-like object.

        Whether the collection contains the given value. This is O(n) and uses - equals (==) to test the existence.

        Parameters
        col: Object
        The collection-like object.
        val: *
        The value to check for.
        Returns
        True if the map contains the value.
        code »<T, S> goog.structs.every ( col, f, opt_obj )boolean

        Calls f for each value in a collection. If all calls return true this return +goog.structs

        Namespace goog.structs

        code »

        Interfaces

        goog.structs.Collection
        An interface for a collection of values.

        Classes

        goog.structs.Map
        Class for Hash Map datastructure.
        goog.structs.Set
        A set that can contain both primitives and objects.
        Show:

        Global Functions

        Removes all the elements from the collection.

        Parameters
        col: Object
        The collection-like object.

        Whether the collection contains the given value. This is O(n) and uses + equals (==) to test the existence.

        Parameters
        col: Object
        The collection-like object.
        val: *
        The value to check for.
        Returns
        True if the map contains the value.
        code »<T, S> goog.structs.every ( col, f, opt_obj )boolean

        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.

        Parameters
        col: S
        The collection-like object.
        f: function(this: T, ?, ?, S): boolean
        The function to call for every value. This function takes 3 arguments (the value, the key or undefined if the collection has no notion of keys, and the collection) and should return a boolean.
        opt_obj: T=
        The object to be used as the value of 'this' - within f.
        Returns
        True if all key-value pairs pass the test.
        code »<T, S> goog.structs.filter ( col, f, opt_obj )(!Object|!Array)

        Calls a function for every value in the collection. When a call returns true, + within f.Returns

        True if all key-value pairs pass the test.
        code »<T, S> goog.structs.filter ( col, f, opt_obj )(!Object|!Array)

        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).

        Parameters
        col: S
        The collection-like object.
        f: function(this: T, ?, ?, S): boolean
        The function to call for every value. This function takes 3 arguments (the value, the key or undefined if the collection has no @@ -14,22 +14,25 @@ is false the value is not included.
        opt_obj: T=
        The object to be used as the value of 'this' within 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.
        code »<T, S> goog.structs.forEach ( col, f, opt_obj )

        Calls a function for each value in a collection. The function takes - three arguments; the value, the key and the collection.

        Parameters
        col: S
        The collection-like object.
        f: function(this: T, ?, ?, S): ?
        The function to call for every value. + has keys and values a plain old JS object is returned.
        code »<T, S> goog.structs.forEach ( col, f, opt_obj )

        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.

        Parameters
        col: S
        The collection-like object.
        f: function(this: T, ?, ?, S): ?
        The function to call for every value. This function takes 3 arguments (the value, the key or undefined if the collection has no notion of keys, and the collection) and the return value is irrelevant.
        opt_obj: T=
        The object to be used as the value of 'this' within f.

        Returns the number of values in the collection-like object.

        Parameters
        col: Object
        The collection-like object.
        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.

        Parameters
        col: Object
        The collection-like object.
        Returns
        The keys in the collection.

        Returns the values of the collection-like object.

        Parameters
        col: Object
        The collection-like object.
        Returns
        The values in the collection-like object.

        Whether the collection is empty.

        Parameters
        col: Object
        The collection-like object.
        Returns
        True if empty.
        code »<T, S, V> goog.structs.map ( col, f, opt_obj )(!Object.<V>|!Array.<V>)

        Calls a function for every value in the collection and adds the result into a + keys/indexes and this function will return undefined in those cases.

        Parameters
        col: Object
        The collection-like object.
        Returns
        The keys in the collection.

        Returns the values of the collection-like object.

        Parameters
        col: Object
        The collection-like object.
        Returns
        The values in the collection-like object.

        Whether the collection is empty.

        Parameters
        col: Object
        The collection-like object.
        Returns
        True if empty.
        code »<T, S, V> goog.structs.map ( col, f, opt_obj )(!Object.<V>|!Array.<V>)

        Calls a function for every value in the collection and adds the result into a new collection (defaults to creating a new Array).

        Parameters
        col: S
        The collection-like object.
        f: function(this: T, ?, ?, S): V
        The function to call for every value. This function takes 3 arguments (the value, the key or undefined if the collection has no notion of keys, and the collection) and should return something. The result will be used as the value in the new collection.
        opt_obj: T=
        The object to be used as the value of 'this' within 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.
        code »<T, S> goog.structs.some ( col, f, opt_obj )boolean

        Calls f for each value in a collection. If any call returns true this returns + values a plain old JS object is returned.

        code »<T, S> goog.structs.some ( col, f, opt_obj )boolean

        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.

        Parameters
        col: S
        The collection-like object.
        f: function(this: T, ?, ?, S): boolean
        The function to call for every value. This function takes 3 arguments (the value, the key or undefined if the collection has no notion of keys, and the collection) and should return a boolean.
        opt_obj: T=
        The object to be used as the value of 'this' - within f.
        Returns
        True if any value passes the test.
        \ No newline at end of file + within f.Returns
        True if any value passes the test.
        \ No newline at end of file diff --git a/docs/namespace_goog_style.html b/docs/namespace_goog_style.html new file mode 100644 index 0000000..0c25e81 --- /dev/null +++ b/docs/namespace_goog_style.html @@ -0,0 +1,211 @@ +goog.style

        Namespace goog.style

        code »
        Show:

        Global Functions

        Clears the background image of an element in a browser independent manner.

        Parameters
        el: Element
        The element to clear background image for.

        Call fn on element such that element's dimensions are + accurate when it's passed to fn.

        Parameters
        fn: function(!Element): T
        Function to call with element as + an argument after temporarily changing element's display such + that its dimensions are accurate.
        element: !Element
        Element (which may have display none) to use as + argument to fn.
        Returns
        Value returned by calling fn with element.

        Retrieves the computed background color string for a given element. The + string returned is suitable for assigning to another element's + background-color, but is not guaranteed to be in any particular string + format. Accessing the color in a numeric form may not be possible in all + browsers or with all input. + + If the background color for the element is defined as a hexadecimal value, + the resulting string can be parsed by goog.color.parse in all supported + browsers. + + Whether named colors like "red" or "lightblue" get translated into a + format which can be parsed is browser dependent. Calling this function on + transparent elements will return "transparent" in most browsers or + "rgba(0, 0, 0, 0)" in WebKit.

        Parameters
        element: Element
        The element to get the background color of.
        Returns
        The computed string value of the background color.

        Gets the computed border widths (on all sides) in pixels

        Parameters
        element: Element
        The element to get the border widths for.
        Returns
        The computed border widths.

        Gets the border box size for an element.

        Parameters
        element: Element
        The element to get the size for.
        Returns
        The border box size.

        Gets the client rectangle of the DOM element. + + getBoundingClientRect is part of a new CSS object model draft (with a + long-time presence in IE), replacing the error-prone parent offset + computation and the now-deprecated Gecko getBoxObjectFor. + + This utility patches common browser bugs in getBoundingClientRect. It + will fail if getBoundingClientRect is unsupported. + + If the element is not in the DOM, the result is undefined, and an error may + be thrown depending on user agent.

        Parameters
        el: !Element
        The element whose bounding rectangle is being queried.
        Returns
        A native bounding rectangle with numerical left, top, + right, and bottom. Reported by Firefox to be of object type ClientRect.

        Returns a bounding rectangle for a given element in page space.

        Parameters
        element: Element
        Element to get bounds of. Must not be display none.
        Returns
        Bounding rectangle for the element.
        code »goog.style.getBox_ ( element, stylePrefix )!goog.math.Box

        Gets the computed paddings or margins (on all sides) in pixels.

        Parameters
        element: Element
        The element to get the padding for.
        stylePrefix: string
        Pass 'padding' to retrieve the padding box, + or 'margin' to retrieve the margin box.
        Returns
        The computed paddings or margins.

        Gets the cascaded style value of a node, or null if the value cannot be + computed (only Internet Explorer can do this).

        Parameters
        element: Element
        Element to get style of.
        style: string
        Property to get (camel-case).
        Returns
        Style value.

        Returns clientLeft (width of the left border and, if the directionality is + right to left, the vertical scrollbar) and clientTop as a coordinate object.

        Parameters
        el: Element
        Element to get clientLeft for.
        Returns
        Client left and top.

        Returns the position of the event or the element's border box relative to + the client viewport.

        Parameters
        el: (Element|Event|goog.events.Event)
        Element or a mouse / touch event.
        Returns
        The position.

        Returns the position of the event or the element's border box relative to + the client viewport.

        Parameters
        el: !Element
        Element whose position to get.
        Returns
        The position.

        Returns the viewport element for a particular document

        Parameters
        opt_node: Node=
        DOM node (Document is OK) to get the viewport element + of.
        Returns
        document.documentElement or document.body.

        Retrieves the computed value of the box-sizing CSS attribute. + Browser support: http://caniuse.com/css3-boxsizing.

        Parameters
        element: !Element
        The element whose box-sizing to get.
        Returns
        'content-box', 'border-box' or 'padding-box'. null if + box-sizing is not supported (IE7 and below).

        Retrieves the computed value of the cursor CSS attribute.

        Parameters
        element: Element
        The element to get the cursor of.
        Returns
        The computed string value of the cursor attribute.

        Retrieves the computed value of the overflow-x CSS attribute.

        Parameters
        element: Element
        The element to get the overflow-x of.
        Returns
        The computed string value of the overflow-x attribute.

        Retrieves the computed value of the overflow-y CSS attribute.

        Parameters
        element: Element
        The element to get the overflow-y of.
        Returns
        The computed string value of the overflow-y attribute.

        Retrieves the computed value of the position CSS attribute.

        Parameters
        element: Element
        The element to get the position of.
        Returns
        Position value.
        code »goog.style.getComputedStyle ( element, property )string

        Retrieves a computed style value of a node. It returns empty string if the + value cannot be computed (which will be the case in Internet Explorer) or + "none" if the property requested is an SVG one and it has not been + explicitly set (firefox and webkit).

        Parameters
        element: Element
        Element to get style of.
        property: string
        Property to get (camel-case).
        Returns
        Style value.

        Retrieves the computed value of the text-align CSS attribute.

        Parameters
        element: Element
        The element to get the text-align of.
        Returns
        The computed string value of the text-align attribute.

        Retrieves the computed value of the CSS transform attribute.

        Parameters
        element: Element
        The element to get the transform of.
        Returns
        The computed string representation of the transform matrix.

        Retrieves the computed value of the z-index CSS attribute.

        Parameters
        element: Element
        The element to get the z-index of.
        Returns
        The computed value of the z-index attribute.

        Calculate the scroll position of container with the minimum amount so + that the content and the borders of the given element become visible. + If the element is bigger than the container, its top left corner will be + aligned as close to the container's top left corner as possible.

        Parameters
        element: Element
        The element to make visible.
        container: Element
        The container to scroll.
        opt_center: boolean=
        Whether to center the element in the container. + Defaults to false.
        Returns
        The new scroll position of the container, + in form of goog.math.Coordinate(scrollLeft, scrollTop).

        Gets the content box size for an element. This is potentially expensive in + all browsers.

        Parameters
        element: Element
        The element to get the size for.
        Returns
        The content box size.

        Returns the x,y translation component of any CSS transforms applied to the + element, in pixels.

        Parameters
        element: !Element
        The element to get the translation of.
        Returns
        The CSS translation of the element in px.

        Gets value of explicitly-set float CSS property on an element.

        Parameters
        el: Element
        The element to get float property of.
        Returns
        The value of explicitly-set float CSS property on this + element.

        Returns the font face applied to a given node. Opera and IE should return + the font actually displayed. Firefox returns the author's most-preferred + font (whether the browser is capable of displaying it or not.)

        Parameters
        el: Element
        The element whose font family is returned.
        Returns
        The font family applied to el.

        Returns the font size, in pixels, of text in an element.

        Parameters
        el: Element
        The element whose font size is returned.
        Returns
        The font size (in pixels).

        Returns a Coordinate object relative to the top-left of an HTML document + in an ancestor frame of this element. Used for measuring the position of + an element inside a frame relative to a containing frame.

        Parameters
        el: Element
        Element to get the page offset for.
        relativeWin: Window
        The window to measure relative to. If relativeWin + is not in the ancestor frame chain of the element, we measure relative to + the top-most window.
        Returns
        The page offset.

        Helper function for IE to get the pixel border.

        Parameters
        element: Element
        The element to get the pixel border for.
        prop: string
        The part of the property name.
        Returns
        The value in pixels.

        Helper function for getting the pixel padding or margin for IE.

        Parameters
        element: Element
        The element to get the padding for.
        propName: string
        The property name.
        Returns
        The pixel padding.
        code »goog.style.getIePixelValue_ ( element, value, name, pixelName )number

        IE specific function that converts a non pixel unit to pixels.

        Parameters
        element: Element
        The element to convert the value for.
        value: string
        The current value as a string. The value must not be + ''.
        name: string
        The CSS property name to use for the converstion. This + should be 'left', 'top', 'width' or 'height'.
        pixelName: string
        The CSS pixel property name to use to get the + value in pixels.
        Returns
        The value in pixels.

        Returns the units used for a CSS length measurement.

        Parameters
        value: string
        A CSS length quantity.
        Returns
        The units of measurement.

        Gets the computed margins (on all sides) in pixels.

        Parameters
        element: Element
        The element to get the margins for.
        Returns
        The computed margins.

        Returns the first parent that could affect the position of a given element.

        Parameters
        element: Element
        The element to get the offset parent for.
        Returns
        The first offset parent or null if one cannot be found.

        Gets the opacity of a node (x-browser). This gets the inline style opacity + of the node, and does not take into account the cascaded or the computed + style for this node.

        Parameters
        el: Element
        Element whose opacity has to be found.
        Returns
        Opacity between 0 and 1 or an empty string '' + if the opacity is not set.

        Gets the computed paddings (on all sides) in pixels.

        Parameters
        element: Element
        The element to get the padding for.
        Returns
        The computed paddings.

        Returns a Coordinate object relative to the top-left of the HTML document. + Implemented as a single function to save having to do two recursive loops in + opera and safari just to get both coordinates. If you just want one value do + use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but + note if you call both those methods the tree will be analysed twice.

        Parameters
        el: Element
        Element to get the page offset for.
        Returns
        The page offset.

        Returns the left coordinate of an element relative to the HTML document

        Parameters
        el: Element
        Elements.
        Returns
        The left coordinate.

        Returns the top coordinate of an element relative to the HTML document

        Parameters
        el: Element
        Elements.
        Returns
        The top coordinate.

        Helper function to create a string to be set into a pixel-value style + property of an element. Can round to the nearest integer value.

        Parameters
        value: (string|number)
        The style value to be used. If a number, + 'px' will be appended, otherwise the value will be applied directly.
        round: boolean
        Whether to round the nearest integer (if property + is a number).
        Returns
        The string value for the property.

        Gets the offsetLeft and offsetTop properties of an element and returns them + in a Coordinate object

        Parameters
        element: Element
        Element.
        Returns
        The position.

        Returns the position of an element relative to another element in the + document. A relative to B

        Parameters
        a: (Element|Event|goog.events.Event)
        Element or mouse event whose + position we're calculating.
        b: (Element|Event|goog.events.Event)
        Element or mouse event position + is relative to.
        Returns
        The relative position.

        Returns the scroll bar width (represents the width of both horizontal + and vertical scroll).

        Parameters
        opt_className: string=
        An optional class name (or names) to apply + to the invisible div created to measure the scrollbar. This is necessary + if some scrollbars are styled differently than others.
        Returns
        The scroll bar width in px.

        Gets the height and width of an element, even if its display is none. + + Specifically, this returns the height and width of the border box, + irrespective of the box model in effect. + + Note that this function does not take CSS transforms into account. Please see + goog.style.getTransformedSize.

        Parameters
        element: Element
        Element to get size of.
        Returns
        Object with width/height properties.

        Gets the height and width of an element when the display is not none.

        Parameters
        element: Element
        Element to get size of.
        Returns
        Object with width/height properties.
        code »goog.style.getStyle ( element, property )string

        Retrieves an explicitly-set style value of a node. This returns '' if there + isn't a style attribute on the element or if this style property has not been + explicitly set in script.

        Parameters
        element: Element
        Element to get style of.
        property: string
        Property to get, css-style (if you have a camel-case + property, use element.style[style]).
        Returns
        Style value.
        code »goog.style.getStyle_ ( element, style )string

        Cross-browser pseudo get computed style. It returns the computed style where + available. If not available it tries the cascaded style value (IE + currentStyle) and in worst case the inline style value. It shouldn't be + called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for + discussion.

        Parameters
        element: Element
        Element to get style of.
        style: string
        Property to get (must be camelCase, not css-style.).
        Returns
        Style value.

        Gets the height and width of an element, post transform, even if its display + is none. + + This is like goog.style.getSize, except: +

          +
        1. Takes webkitTransforms such as rotate and scale into account. +
        2. Will return null if element doesn't respond to + getBoundingClientRect. +
        3. Currently doesn't make sense on non-WebKit browsers which don't support + webkitTransforms. +
        Parameters
        element: !Element
        Element to get size of.
        Returns
        Object with width/height properties.

        Returns the style property name in camel-case. If it does not exist and a + vendor-specific version of the property does exist, then return the vendor- + specific property name instead.

        Parameters
        element: Element
        The element to change.
        style: string
        Style name.
        Returns
        Vendor-specific style.

        Returns the style property name in CSS notation. If it does not exist and a + vendor-specific version of the property does exist, then return the vendor- + specific property name instead.

        Parameters
        element: Element
        The element to change.
        style: string
        Style name.
        Returns
        Vendor-specific style.

        Calculates the viewport coordinates relative to the page/document + containing the node. The viewport may be the browser viewport for + non-iframe document, or the iframe container for iframe'd document.

        Parameters
        doc: !Document
        The document to use as the reference point.
        Returns
        The page offset of the viewport.

        Calculates and returns the visible rectangle for a given element. Returns a + box describing the visible portion of the nearest scrollable offset ancestor. + Coordinates are given relative to the document.

        Parameters
        element: Element
        Element to get the visible rect for.
        Returns
        Bounding elementBox describing the visible rect or + null if scrollable ancestor isn't inside the visible viewport.
        code »goog.style.installStyles ( stylesString, opt_node )(Element|StyleSheet)

        Installs the styles string into the window that contains opt_element. If + opt_element is null, the main window is used.

        Parameters
        stylesString: string
        The style string to install.
        opt_node: Node=
        Node whose parent document should have the + styles installed.
        Returns
        The style element created.

        Test whether the given element has been shown or hidden via a call to + #setElementShown. + + Note this is strictly a companion method for a call + to #setElementShown and the same caveats apply; in particular, this + method does not guarantee that the return value will be consistent with + whether or not the element is actually visible.

        Parameters
        el: Element
        The element to test.
        Returns
        Whether the element has been shown.

        Returns true if the element is using right to left (rtl) direction.

        Parameters
        el: Element
        The element to test.
        Returns
        True for right to left, false for left to right.

        Returns true if the element is set to be unselectable, false otherwise. + Note that on some platforms (e.g. Mozilla), even if an element isn't set + to be unselectable, it will behave as such if any of its ancestors is + unselectable.

        Parameters
        el: Element
        Element to check.
        Returns
        Whether the element is set to be unselectable.

        Parses a style attribute value. Converts CSS property names to camel case.

        Parameters
        value: string
        The style attribute value.
        Returns
        Map of CSS properties to string values.
        code »goog.style.scrollIntoContainerView ( element, container, opt_center )

        Changes the scroll position of container with the minimum amount so + that the content and the borders of the given element become visible. + If the element is bigger than the container, its top left corner will be + aligned as close to the container's top left corner as possible.

        Parameters
        element: Element
        The element to make visible.
        container: Element
        The container to scroll.
        opt_center: boolean=
        Whether to center the element in the container. + Defaults to false.

        Sets the border box size of an element. This is potentially expensive in IE + if the document is CSS1Compat mode

        Parameters
        element: Element
        The element to set the size on.
        size: goog.math.Size
        The new size.
        code »goog.style.setBoxSizingSize_ ( element, size, boxSizing )

        Helper function that sets the box sizing as well as the width and height

        Parameters
        element: Element
        The element to set the size on.
        size: goog.math.Size
        The new size to set.
        boxSizing: string
        The box-sizing value.

        Sets the content box size of an element. This is potentially expensive in IE + if the document is BackCompat mode.

        Parameters
        element: Element
        The element to set the size on.
        size: goog.math.Size
        The new size.

        Shows or hides an element from the page. Hiding the element is done by + setting the display property to "none", removing the element from the + rendering hierarchy so it takes up no space. To show the element, the default + inherited display property is restored (defined either in stylesheets or by + the browser's default style rules). + + Caveat 1: if the inherited display property for the element is set to "none" + by the stylesheets, that is the property that will be restored by a call to + setElementShown(), effectively toggling the display between "none" and + "none". + + Caveat 2: if the element display style is set inline (by setting either + element.style.display or a style attribute in the HTML), a call to + setElementShown will clear that setting and defer to the inherited style in + the stylesheet.

        Parameters
        el: Element
        Element to show or hide.
        isShown: *
        True to render the element in its default style, + false to disable rendering the element.

        Sets CSS float property on an element.

        Parameters
        el: Element
        The element to set float property on.
        value: string
        The value of float CSS property to set on this element.
        code »goog.style.setHeight ( element, height )

        Set the height of an element. Sets the element's style property.

        Parameters
        element: Element
        Element to set the height of.
        height: (string|number)
        The height value to set. If a number, 'px' + will be appended, otherwise the value will be applied directly.

        Sets 'display: inline-block' for an element (cross-browser).

        Parameters
        el: Element
        Element to which the inline-block display style is to be + applied.

        Sets the opacity of a node (x-browser).

        Parameters
        el: Element
        Elements whose opacity has to be set.
        alpha: (number|string)
        Opacity between 0 and 1 or an empty string + '' to clear the opacity.

        Moves an element to the given coordinates relative to the client viewport.

        Parameters
        el: Element
        Absolutely positioned element to set page offset for. + It must be in the document.
        x: (number|goog.math.Coordinate)
        Left position of the element's margin + box or a coordinate object.
        opt_y: number=
        Top position of the element's margin box.
        code »goog.style.setPosition ( el, arg1, opt_arg2 )

        Sets the top/left values of an element. If no unit is specified in the + argument then it will add px. The second argument is required if the first + argument is a string or number and is ignored if the first argument + is a coordinate.

        Parameters
        el: Element
        Element to move.
        arg1: (string|number|goog.math.Coordinate)
        Left position or coordinate.
        opt_arg2: (string|number)=
        Top position.

        Sets 'white-space: pre-wrap' for a node (x-browser). + + There are as many ways of specifying pre-wrap as there are browsers. + + CSS3/IE8: white-space: pre-wrap; + Mozilla: white-space: -moz-pre-wrap; + Opera: white-space: -o-pre-wrap; + IE6/7: white-space: pre; word-wrap: break-word;

        Parameters
        el: Element
        Element to enable pre-wrap for.
        code »goog.style.setSize ( element, w, opt_h )

        Sets the width/height values of an element. If an argument is numeric, + or a goog.math.Size is passed, it is assumed to be pixels and will add + 'px' after converting it to an integer in string form. (This just sets the + CSS width and height properties so it might set content-box or border-box + size depending on the box model the browser is using.)

        Parameters
        element: Element
        Element to set the size of.
        w: (string|number|goog.math.Size)
        Width of the element, or a + size object.
        opt_h: (string|number)=
        Height of the element. Required if w is not a + size object.
        code »goog.style.setStyle ( element, style, opt_value )

        Sets a style value on an element. + + This function is not indended to patch issues in the browser's style + handling, but to allow easy programmatic access to setting dash-separated + style properties. An example is setting a batch of properties from a data + object without overwriting old styles. When possible, use native APIs: + elem.style.propertyKey = 'value' or (if obliterating old styles is fine) + elem.style.cssText = 'property1: value1; property2: value2'.

        Parameters
        element: Element
        The element to change.
        style: (string|Object)
        If a string, a style name. If an object, a hash + of style names to style values.
        opt_value: (string|number|boolean)=
        If style was a string, then this + should be the value.
        code »goog.style.setStyle_ ( element, value, style )

        Sets a style value on an element, with parameters swapped to work with + goog.object.forEach(). Prepends a vendor-specific prefix when + necessary.

        Parameters
        element: Element
        The element to change.
        value: (string|number|boolean|undefined)
        Style value.
        style: string
        Style name.
        code »goog.style.setStyles ( element, stylesString )

        Sets the content of a style element. The style element can be any valid + style element. This element will have its content completely replaced by + the new stylesString.

        Parameters
        element: (Element|StyleSheet)
        A stylesheet element as returned by + installStyles.
        stylesString: string
        The new content of the stylesheet.

        Sets the background of an element to a transparent image in a browser- + independent manner. + + This function does not support repeating backgrounds or alternate background + positions to match the behavior of Internet Explorer. It also does not + support sizingMethods other than crop since they cannot be replicated in + browsers other than Internet Explorer.

        Parameters
        el: Element
        The element to set background on.
        src: string
        The image source URL.
        code »goog.style.setUnselectable ( el, unselectable, opt_noRecurse )

        Makes the element and its descendants selectable or unselectable. Note + that on some platforms (e.g. Mozilla), even if an element isn't set to + be unselectable, it will behave as such if any of its ancestors is + unselectable.

        Parameters
        el: Element
        The element to alter.
        unselectable: boolean
        Whether the element and its descendants + should be made unselectable.
        opt_noRecurse: boolean=
        Whether to only alter the element's own + selectable state, and leave its descendants alone; defaults to false.
        code »goog.style.setWidth ( element, width )

        Set the width of an element. Sets the element's style property.

        Parameters
        element: Element
        Element to set the width of.
        width: (string|number)
        The width value to set. If a number, 'px' + will be appended, otherwise the value will be applied directly.
        Deprecated: Use goog.style.setElementShown instead.

        Shows or hides an element from the page. Hiding the element is done by + setting the display property to "none", removing the element from the + rendering hierarchy so it takes up no space. To show the element, the default + inherited display property is restored (defined either in stylesheets or by + the browser's default style rules.) + + Caveat 1: if the inherited display property for the element is set to "none" + by the stylesheets, that is the property that will be restored by a call to + showElement(), effectively toggling the display between "none" and "none". + + Caveat 2: if the element display style is set inline (by setting either + element.style.display or a style attribute in the HTML), a call to + showElement will clear that setting and defer to the inherited style in the + stylesheet.

        Parameters
        el: Element
        Element to show or hide.
        display: *
        True to render the element in its default style, + false to disable rendering the element.
        Deprecated: Use goog.string.toCamelCase instead.

        Converts a CSS selector in the form style-property to styleProperty.

        Parameters
        selector: *
        CSS Selector.
        Returns
        Camel case selector.
        Deprecated: Use goog.string.toSelectorCase instead.

        Converts a CSS selector in the form styleProperty to style-property.

        Parameters
        selector: string
        Camel case selector.
        Returns
        Selector cased.

        Reverse of parseStyleAttribute; that is, takes a style object and returns the + corresponding attribute value. Converts camel case property names to proper + CSS selector names.

        Parameters
        obj: Object
        Map of CSS properties to values.
        Returns
        The style attribute value.

        Translates the specified rect relative to origBase page, for newBase page. + If origBase and newBase are the same, this function does nothing.

        Parameters
        rect: goog.math.Rect
        The source rectangle relative to origBase page, + and it will have the translated result.
        origBase: goog.dom.DomHelper
        The DomHelper for the input rectangle.
        newBase: goog.dom.DomHelper
        The DomHelper for the resultant + coordinate. This must be a DOM for an ancestor frame of origBase + or the same as origBase.

        Removes the styles added by #installStyles.

        Parameters
        styleSheet: (Element|StyleSheet)
        The value returned by + #installStyles.

        Global Properties

        Map of absolute CSS length units

        Map of relative CSS length units that can be accurately converted to px + font-size values using getIePixelValue_. Only units that are defined in + relation to a font size are convertible (%, small, etc. are not).

        Regular expression to extract x and y translation components from a CSS + transform Matrix representation.

        A map used to map the border width keywords to a pixel width.

        Regular expression used for getLengthUnits.

        The CSS style property corresponding to an element being + unselectable on the current browser platform (null if none). + Opera and IE instead use a DOM attribute 'unselectable'.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_testing.html b/docs/namespace_goog_testing.html new file mode 100644 index 0000000..f20920e --- /dev/null +++ b/docs/namespace_goog_testing.html @@ -0,0 +1,28 @@ +goog.testing

        Namespace goog.testing

        code »

        Interfaces

        Classes

        goog.testing.AsyncTestCase
        A test case that is capable of running tests the contain asynchronous logic.
        goog.testing.FunctionCall
        Struct for a single function call.
        goog.testing.JsUnitException
        No Description.
        goog.testing.LooseExpectationCollection
        This class is an ordered collection of expectations for one method.
        goog.testing.LooseMock
        This is a mock that does not care about the order of method calls.
        goog.testing.Mock
        The base class for a mock object.
        goog.testing.MockClock
        Class for unit testing code that uses setTimeout and clearTimeout.
        goog.testing.MockControl
        Controls a set of mocks.
        goog.testing.MockExpectation
        This is a class that represents an expectation.
        goog.testing.ObjectPropertyString
        Object to pass a property name as a string literal and its containing object + when the JSCompiler is rewriting these names.
        goog.testing.PropertyReplacer
        Helper class for stubbing out variables and object properties for unit tests.
        goog.testing.StrictMock
        This is a mock that verifies that methods are called in the order that they + are specified during the recording phase.
        goog.testing.TestCase
        A class representing a JsUnit test case.
        goog.testing.TestRunner
        Construct a test runner.
        Show:

        Global Functions

        code »goog.testing.FunctionMock ( opt_functionName, opt_strictness )goog.testing.MockInterface

        Class used to mock a function. Useful for mocking closures and anonymous + callbacks etc. Creates a function object that extends goog.testing.Mock.

        Parameters
        opt_functionName: string=
        The optional name of the function to mock. + Set to '[anonymous mocked function]' if not passed in.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked function.

        Mocks a global / top-level function. Creates a goog.testing.MethodMock + in the global scope with the name specified by functionName.

        Parameters
        functionName: string
        The name of the function we're going to mock.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked global function.
        code »goog.testing.MethodMock ( scope, functionName, opt_strictness )!goog.testing.MockInterface

        Mocks an existing function. Creates a goog.testing.FunctionMock + and registers it in the given scope with the name specified by functionName.

        Parameters
        scope: Object
        The scope of the method to be mocked out.
        functionName: string
        The name of the function we're going to mock.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked method.
        code »goog.testing.createConstructorMock ( scope, constructorName, opt_strictness )!goog.testing.MockInterface

        Convenience method for creating a mock for a constructor. Copies class + members to the mock. + +

        When mocking a constructor to return a mocked instance, remember to create + the instance mock before mocking the constructor. If you mock the constructor + first, then the mock framework will be unable to examine the prototype chain + when creating the mock instance.

        Parameters
        scope: Object
        The scope of the constructor to be mocked out.
        constructorName: string
        The name of the constructor we're going to + mock.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked constructor.

        Convenience method for creating a mock for a function.

        Parameters
        opt_functionName: string=
        The optional name of the function to mock + set to '[anonymous mocked function]' if not passed in.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked function.

        Convenience method for creating a mocks for a global / top-level function.

        Parameters
        functionName: string
        The name of the function we're going to mock.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked global function.
        code »goog.testing.createMethodMock ( scope, functionName, opt_strictness )!goog.testing.MockInterface

        Convenience method for creating a mock for a method.

        Parameters
        scope: Object
        The scope of the method to be mocked out.
        functionName: string
        The name of the function we're going to mock.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked global function.

        Same as goog.testing.recordFunction but the recorded function will + have the same prototype and static fields as the original one. It can be + used with constructors.

        Parameters
        ctor: !Function
        The function to wrap and record.
        Returns
        The wrapped function.

        Wraps the function into another one which calls the inner function and + records its calls. The recorded function will have 3 static methods: + getCallCount, getCalls and getLastCall but won't + inherit the original function's prototype and static fields.

        Parameters
        opt_f: !Function=
        The function to wrap and record. Defaults to + goog.nullFunction.
        Returns
        The wrapped function.
        \ No newline at end of file diff --git a/docs/namespace_goog_testing_MethodMock.html b/docs/namespace_goog_testing_MethodMock.html new file mode 100644 index 0000000..6cc86c6 --- /dev/null +++ b/docs/namespace_goog_testing_MethodMock.html @@ -0,0 +1,3 @@ +goog.testing.MethodMock

        Namespace goog.testing.MethodMock

        code »

        Mocks an existing function. Creates a goog.testing.FunctionMock + and registers it in the given scope with the name specified by functionName.

        Main

        MethodMock ( scope, functionName, opt_strictness )!goog.testing.MockInterface
        Parameters
        scope: Object
        The scope of the method to be mocked out.
        functionName: string
        The name of the function we're going to mock.
        opt_strictness: number=
        One of goog.testing.Mock.LOOSE or + goog.testing.Mock.STRICT. The default is STRICT.
        Returns
        The mocked method.
        Show:

        Global Functions

        Resets the global function that we mocked back to its original state.

        \ No newline at end of file diff --git a/docs/namespace_goog_testing_asserts.html b/docs/namespace_goog_testing_asserts.html new file mode 100644 index 0000000..7eeb88e --- /dev/null +++ b/docs/namespace_goog_testing_asserts.html @@ -0,0 +1,15 @@ +goog.testing.asserts

        Namespace goog.testing.asserts

        code »
        Show:

        Type Definitions

        Global Functions

        Runs a function in an environment where test failures are not logged. This is + useful for testing test code, where failures can be a normal part of a test.

        Parameters
        fn: function(): void
        Function to run without logging failures.
        code »goog.testing.asserts.contains_ ( container, contained )boolean

        Tells whether the array contains the given element.

        Parameters
        container: goog.testing.asserts.ArrayLike
        The array to + find the element in.
        contained: *
        Element to find.
        Returns
        Whether the element is in the array.
        code »goog.testing.asserts.findDifferences ( expected, actual, opt_equalityPredicate )?string

        Determines if two items of any type match, and formulates an error message + if not.

        Parameters
        expected: *
        Expected argument to match.
        actual: *
        Argument as a result of performing the test.
        opt_equalityPredicate: (function(string, *, *): ?string)=
        An optional + function that can be used to check equality of variables. It accepts 3 + arguments: type-of-variables, var1, var2 (in that order) and returns an + error message if the variables are not equal, + goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL if the variables + are equal, or + goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS if the predicate + couldn't check the input variables. The function will be called only if + the types of var1 and var2 are identical.
        Returns
        Null on success, error message on failure.
        Parameters
        expected: *
        The expected value.
        actual: *
        The actual value.
        Returns
        A failure message of the values don't match.
        code »goog.testing.asserts.indexOf_ ( container, contained )number

        Finds the position of the first occurrence of an element in a container.

        Parameters
        container: goog.testing.asserts.ArrayLike
        The array to find the element in.
        contained: *
        Element to find.
        Returns
        Index of the first occurrence or -1 if not found.

        Helper function for assertObjectEquals.

        Parameters
        prop: string
        A property name.
        Returns
        If the property name is an array index.

        Compares equality of two numbers, allowing them to differ up to a given + tolerance.

        Parameters
        var1: number
        A number.
        var2: number
        A number.
        tolerance: number
        the maximum allowed difference.
        Returns
        Whether the two variables are sufficiently close.

        Raises a JsUnit exception with the given comment.

        Parameters
        comment: string
        A summary for the exception.
        opt_message: string=
        A description of the exception.

        Converts an array like object to array or clones it if it's already array.

        Parameters
        arrayLike: goog.testing.asserts.ArrayLike
        The collection.
        Returns
        Copy of the collection as array.

        Global Properties

        The return value of the equality predicate passed to findDifferences below, + in cases where the predicate can't test the input variables for equality.

        The return value of the equality predicate passed to findDifferences below, + in cases where the input vriables are equal.

        \ No newline at end of file diff --git a/docs/namespace_goog_testing_events.html b/docs/namespace_goog_testing_events.html new file mode 100644 index 0000000..930c01f --- /dev/null +++ b/docs/namespace_goog_testing_events.html @@ -0,0 +1,80 @@ +goog.testing.events

        Namespace goog.testing.events

        code »

        Classes

        goog.testing.events.Event
        goog.events.BrowserEvent expects an Event so we provide one for JSCompiler.
        Show:

        Global Functions

        Asserts an event target exists. This will fail if target is not defined. + + TODO(nnaze): Gradually add this to the methods in this file, and eventually + update the method signatures to not take nullables. See http://b/8961907

        Parameters
        target: EventTarget
        A target to assert.
        Returns
        The target, guaranteed to exist.

        Simulate a blur event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        Returns
        The value returned by firing the blur browser event, + which returns false iff 'preventDefault' was invoked.

        Simulates an event's capturing and bubbling phases.

        Parameters
        event: Event
        A simulated native event. It will be wrapped in a + normalized BrowserEvent and dispatched to Closure listeners on all + ancestors of its target (inclusive).
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireClickEvent ( target, opt_button, opt_coords, opt_eventProperties )boolean

        Simulates a click event on the given target. IE only supports click with + the left mouse button.

        Parameters
        target: EventTarget
        The target for the event.
        opt_button: goog.events.BrowserEvent.MouseButton=
        Mouse button; + defaults to goog.events.BrowserEvent.MouseButton.LEFT.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireClickSequence ( target, opt_button, opt_coords, opt_eventProperties )boolean

        Simulates a mousedown, mouseup, and then click on the given event target, + with the left mouse button.

        Parameters
        target: EventTarget
        The target for the event.
        opt_button: goog.events.BrowserEvent.MouseButton=
        Mouse button; + defaults to goog.events.BrowserEvent.MouseButton.LEFT.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the sequence: false if preventDefault() + was called on any of the events, true otherwise.

        Simulates a contextmenu event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.

        Simulates a mousedown, contextmenu, and the mouseup on the given event + target, with the right mouse button.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        Returns
        The returnValue of the sequence: false if preventDefault() + was called on any of the events, true otherwise.
        code »goog.testing.events.fireDoubleClickEvent ( target, opt_coords, opt_eventProperties )boolean

        Simulates a double-click event on the given target. Always double-clicks + with the left mouse button since no browser supports double-clicking with + any other buttons.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireDoubleClickSequence ( target, opt_coords, opt_eventProperties )boolean

        Simulates the sequence of events fired by the browser when the user double- + clicks the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the sequence: false if preventDefault() + was called on any of the events, true otherwise.

        Simulate a focus event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        Returns
        The value returned by firing the focus browser event, + which returns false iff 'preventDefault' was invoked.
        code »goog.testing.events.fireKeySequence ( target, keyCode, opt_eventProperties )boolean

        Simulates a complete keystroke (keydown, keypress, and keyup). Note that + if preventDefault is called on the keydown, the keypress will not fire.

        Parameters
        target: EventTarget
        The target for the event.
        keyCode: number
        The keycode of the key pressed.
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the sequence: false if preventDefault() + was called on any of the events, true otherwise.
        code »goog.testing.events.fireMouseButtonEvent_ ( type, target, opt_button, opt_coords, opt_eventProperties )boolean

        Helper function to fire a mouse event. + with the left mouse button since no browser supports double-clicking with + any other buttons.

        Parameters
        type: string
        The event type.
        target: EventTarget
        The target for the event.
        opt_button: number=
        Mouse button; defaults to + goog.events.BrowserEvent.MouseButton.LEFT.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireMouseDownEvent ( target, opt_button, opt_coords, opt_eventProperties )boolean

        Simulates a mousedown event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_button: goog.events.BrowserEvent.MouseButton=
        Mouse button; + defaults to goog.events.BrowserEvent.MouseButton.LEFT.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.

        Simulates a mousemove event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireMouseOutEvent ( target, relatedTarget, opt_coords )boolean

        Simulates a mouseout event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        relatedTarget: EventTarget
        The related target for the event (e.g., + the node that the mouse is being moved into).
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireMouseOverEvent ( target, relatedTarget, opt_coords )boolean

        Simulates a mouseover event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        relatedTarget: EventTarget
        The related target for the event (e.g., + the node that the mouse is being moved out of).
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireMouseUpEvent ( target, opt_button, opt_coords, opt_eventProperties )boolean

        Simulates a mouseup event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_button: goog.events.BrowserEvent.MouseButton=
        Mouse button; + defaults to goog.events.BrowserEvent.MouseButton.LEFT.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireNonAsciiKeySequence ( target, keyCode, keyPressKeyCode, opt_eventProperties )boolean

        Simulates a complete keystroke (keydown, keypress, and keyup) when typing + a non-ASCII character. Same as fireKeySequence, the keypress will not fire + if preventDefault is called on the keydown.

        Parameters
        target: EventTarget
        The target for the event.
        keyCode: number
        The keycode of the keydown and keyup events.
        keyPressKeyCode: number
        The keycode of the keypress event.
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the sequence: false if preventDefault() + was called on any of the events, true otherwise.

        Simulates a popstate event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        state: Object
        History state object.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireTouchEndEvent ( target, opt_coords, opt_eventProperties )boolean

        Simulates a touchend event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Touch position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireTouchMoveEvent ( target, opt_coords, opt_eventProperties )boolean

        Simulates a touchmove event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Touch position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        code »goog.testing.events.fireTouchSequence ( target, opt_coords, opt_eventProperties )boolean

        Simulates a simple touch sequence on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Touch position. Defaults to event + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the sequence: false if preventDefault() + was called on any of the events, true otherwise.
        code »goog.testing.events.fireTouchStartEvent ( target, opt_coords, opt_eventProperties )boolean

        Simulates a touchstart event on the given target.

        Parameters
        target: EventTarget
        The target for the event.
        opt_coords: goog.math.Coordinate=
        Touch position. Defaults to event's + target's position (if available), otherwise (0, 0).
        opt_eventProperties: Object=
        Event properties to be mixed into the + BrowserEvent.
        Returns
        The returnValue of the event: false if preventDefault() was + called on it, true otherwise.
        Parameters
        e: goog.testing.events.Event
        The event.
        Returns
        Whether this is the Gecko/Mac's Meta-C/V/X, which + is broken and requires special handling.

        Mixins a listenable into the given object. This turns the object + into a goog.events.Listenable. This is useful, for example, when + you need to mock a implementation of listenable and still want it + to work with goog.events.

        Parameters
        obj: !Object
        The object to mixin into.

        A static helper function that sets the mouse position to the event.

        Parameters
        event: Event
        A simulated native event.
        opt_coords: goog.math.Coordinate=
        Mouse position. Defaults to event's + target's position (if available), otherwise (0, 0).
        \ No newline at end of file diff --git a/docs/namespace_goog_testing_jsunit.html b/docs/namespace_goog_testing_jsunit.html new file mode 100644 index 0000000..df7f963 --- /dev/null +++ b/docs/namespace_goog_testing_jsunit.html @@ -0,0 +1 @@ +goog.testing.jsunit

        Namespace goog.testing.jsunit

        code »
        Show:

        Global Properties

        Base path for JsUnit app files, relative to Closure's base path.

        Filename for the core JS Unit script.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_testing_mockmatchers.html b/docs/namespace_goog_testing_mockmatchers.html new file mode 100644 index 0000000..a44ee24 --- /dev/null +++ b/docs/namespace_goog_testing_mockmatchers.html @@ -0,0 +1,9 @@ +goog.testing.mockmatchers

        Namespace goog.testing.mockmatchers

        code »

        Classes

        goog.testing.mockmatchers.ArgumentMatcher
        A simple interface for executing argument matching.
        goog.testing.mockmatchers.IgnoreArgument
        A matcher that always returns true.
        goog.testing.mockmatchers.InstanceOf
        A matcher that verifies that an argument is an instance of a given class.
        goog.testing.mockmatchers.ObjectEquals
        A matcher that verifies that the argument is an object that equals the given + expected object, using a deep comparison.
        goog.testing.mockmatchers.RegexpMatch
        A matcher that verifies that an argument matches a given RegExp.
        goog.testing.mockmatchers.SaveArgument
        A matcher that saves the argument that it is verifying so that your unit test + can perform extra tests with this argument later.
        goog.testing.mockmatchers.TypeOf
        A matcher that verifies that an argument is of a given type (e.g.
        Show:

        Global Functions

        code »goog.testing.mockmatchers.flexibleArrayMatcher ( expectedArr, arr, opt_expectation )boolean

        A function that checks to see if an array matches a given set of + expectations. The expectations array can be a mix of ArgumentMatcher + implementations and values. True will be returned if values are identical or + if a matcher returns a positive result.

        Parameters
        expectedArr: Array
        An array of expectations which can be either + values to check for equality or ArgumentMatchers.
        arr: Array
        The array to match.
        opt_expectation: ?goog.testing.MockExpectation=
        The expectation + for this match.
        Returns
        Whether or not the given array matches the expectations.

        Global Properties

        An instance of the IgnoreArgument matcher. Returns true for all matches.

        A matcher that verifies that an argument is an array.

        A matcher that verifies that an argument is a array-like. A NodeList is an + example of a collection that is very close to an array.

        A matcher that verifies that an argument is a boolean.

        A matcher that verifies that an argument is a date-like.

        A matcher that verifies that an argument is a function.

        A matcher that verifies that an argument is like a DOM node.

        A matcher that verifies that an argument is a number.

        A matcher that verifies that an argument is an object.

        A matcher that verifies that an argument is a string.

        \ No newline at end of file diff --git a/docs/namespace_goog_testing_stacktrace.html b/docs/namespace_goog_testing_stacktrace.html new file mode 100644 index 0000000..6425540 --- /dev/null +++ b/docs/namespace_goog_testing_stacktrace.html @@ -0,0 +1,29 @@ +goog.testing.stacktrace

        Namespace goog.testing.stacktrace

        code »

        Classes

        goog.testing.stacktrace.Frame
        Class representing one stack frame.
        Show:

        Global Functions

        Converts an array of CallSite (elements of a stack trace in V8) to an array + of Frames.

        Parameters
        stack: !Array
        The stack as an array of CallSites.
        Returns
        The stack as an array of + Frames.

        Brings the stack trace into a common format across browsers.

        Parameters
        stack: string
        Browser-specific stack trace.
        Returns
        Same stack trace in common format.

        Function to deobfuscate function names.

        Creates a stack trace by following the call chain. Based on + goog.debug.getStacktrace.

        Returns
        Stack frames.

        Converts the stack frames into canonical format. Chops the beginning and the + end of it which come from the testing environment, not from the test itself.

        Parameters
        frames: !Array.<goog.testing.stacktrace.Frame>
        The frames.
        Returns
        Canonical, pretty printed stack trace.

        Gets the native stack trace if available otherwise follows the call chain.

        Returns
        The stack trace in canonical format.

        Returns the native stack trace.

        Escapes the special character in HTML.

        Parameters
        text: string
        Plain text.
        Returns
        Escaped text.

        Deobfuscates a compiled function name with the function passed to + #setDeobfuscateFunctionName. Returns the original function name if + the deobfuscator hasn't been set.

        Parameters
        name: string
        The function name to deobfuscate.
        Returns
        The deobfuscated function name.

        Parses a long firefox stack frame.

        Parameters
        frameStr: string
        The stack frame as string.
        Returns
        Stack frame object.

        Parses one stack frame.

        Parameters
        frameStr: string
        The stack frame as string.
        Returns
        Stack frame object or null if the + parsing failed.

        Parses the browser's native stack trace.

        Parameters
        stack: string
        Stack trace.
        Returns
        Stack frames. The + unrecognized frames will be nulled out.

        Sets function to deobfuscate function names.

        Parameters
        fn: function(string): string
        function to deobfuscate function names.

        Global Properties

        RegExp pattern for an URL + line number + column number in V8. + The URL is either in submatch 1 or submatch 2.

        RegExp pattern for function call in the Firefox stack trace. + Creates 2 submatches with function name (optional) and arguments.

        Regular expression for parsing one stack frame in Firefox.

        Regular expression for finding the function name in its source.

        RegExp pattern for JavaScript identifiers. We don't support Unicode + identifiers defined in ECMAScript v3.

        RegExp pattern for function call in a IE stack trace. This expression allows + for identifiers like 'Anonymous function', 'eval code', and 'Global code'.

        Regular expression for parsing a stack frame in IE.

        Maximum number of steps while the call chain is followed.

        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 4 (optional) submatches: the function name (if not anonymous), + the aliased context object and function name (if anonymous), and the + function call arguments.

        Regular expression for parsing on stack frame in Opera 11.68 - 12.17. + Newer versions of Opera use V8 and stack frames should match against + goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_.

        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 a V8 stack trace. + Creates an optional submatch for the namespace identifier including the + "new" keyword for constructor calls (e.g. "new foo.Bar").

        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.

        Regular expression for parsing one stack frame in V8. For more information + on V8 stack frame formats, see + https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi.

        \ No newline at end of file diff --git a/docs/namespace_goog_testing_watchers.html b/docs/namespace_goog_testing_watchers.html new file mode 100644 index 0000000..de31439 --- /dev/null +++ b/docs/namespace_goog_testing_watchers.html @@ -0,0 +1 @@ +goog.testing.watchers

        Namespace goog.testing.watchers

        code »
        Show:

        Global Functions

        Fires clock reset watching functions.

        Enqueues a function to be called when the clock used for setTimeout is reset.

        Parameters
        fn

        Global Properties

        \ No newline at end of file diff --git a/docs/namespace_goog_uri.html b/docs/namespace_goog_uri.html index 2bf8bb8..4570658 100644 --- a/docs/namespace_goog_uri.html +++ b/docs/namespace_goog_uri.html @@ -1 +1 @@ -goog.uri

        Namespace goog.uri

        code »
        Show:
        \ No newline at end of file +goog.uri

        Namespace goog.uri

        code »
        Show:
        \ No newline at end of file diff --git a/docs/namespace_goog_uri_utils.html b/docs/namespace_goog_uri_utils.html index b643ff8..b41e0cb 100644 --- a/docs/namespace_goog_uri_utils.html +++ b/docs/namespace_goog_uri_utils.html @@ -1,4 +1,4 @@ -goog.uri.utils

        Namespace goog.uri.utils

        code »

        Enumerations

        goog.uri.utils.CharCode_
        Character codes inlined to avoid object allocations due to charCode.
        goog.uri.utils.ComponentIndex
        The index of each URI component in the return value of goog.uri.utils.split.
        goog.uri.utils.StandardQueryParam
        Standard supported query parameters.
        Show:

        Type Definitions

        An array representing a set of query parameters with alternating keys +goog.uri.utils

        Namespace goog.uri.utils

        code »

        Enumerations

        goog.uri.utils.CharCode_
        Character codes inlined to avoid object allocations due to charCode.
        goog.uri.utils.ComponentIndex
        The index of each URI component in the return value of goog.uri.utils.split.
        goog.uri.utils.StandardQueryParam
        Standard supported query parameters.
        Show:

        Type Definitions

        An array representing a set of query parameters with alternating keys and values. Keys are assumed to be URI encoded already and live at even indices. See @@ -14,18 +14,18 @@ // Multi-valued param: &house=LosAngeles&house=NewYork&house=null 'house', ['LosAngeles', 'NewYork', null] ]; -
        Supported query parameter values by the parameter serializing utilities. +
        Supported query parameter values by the parameter serializing utilities. If a value is null or undefined, the key-value pair is skipped, as an easy way to omit parameters conditionally. Non-array parameters are converted to a string and URI encoded. Array values are expanded into multiple - &key=value pairs, with each element stringized and URI-encoded.

        Global Functions

        Appends key=value pairs to an array, supporting multi-valued objects.

        Parameters
        key: string
        The key prefix.
        value: goog.uri.utils.QueryValue
        The value to serialize.
        pairs: !Array.<string>
        The array to which the 'key=value' strings - should be appended.
        code »goog.uri.utils.appendParam ( uri, key, opt_value )string

        Appends a single URI parameter. + &key=value pairs, with each element stringized and URI-encoded.

        Global Functions

        Appends key=value pairs to an array, supporting multi-valued objects.

        Parameters
        key: string
        The key prefix.
        value: goog.uri.utils.QueryValue
        The value to serialize.
        pairs: !Array.<string>
        The array to which the 'key=value' strings + should be appended.
        code »goog.uri.utils.appendParam ( uri, key, opt_value )string

        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.

        Parameters
        uri: string
        The original URI, which may already have query data.
        key: string
        The key, which must already be URI encoded.
        opt_value: *=
        The value, which will be stringized and encoded (assumed not already to be encoded). If omitted, undefined, or null, the - key will be added as a valueless parameter.
        Returns
        The URI with the query parameter added.

        Appends URI parameters to an existing URI. + key will be added as a valueless parameter.Returns

        The URI with the query parameter added.

        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, @@ -44,69 +44,70 @@ 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
        uri: string
        The original URI, which may already have query data.
        var_args: ...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)
        An array or argument list conforming to goog.uri.utils.QueryArray.
        Returns
        The URI with all query parameters added.

        Appends query parameters from a map.

        Parameters
        uri: string
        The original URI, which may already have query data.
        map: Object
        An object where keys are URI-encoded parameter keys, + fact that URL's generally can't exceed 2kb.
        Parameters
        uri: string
        The original URI, which may already have query data.
        var_args: ...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)
        An array or argument list conforming to goog.uri.utils.QueryArray.
        Returns
        The URI with all query parameters added.

        Appends query parameters from a map.

        Parameters
        uri: string
        The original URI, which may already have query data.
        map: Object
        An object where keys are URI-encoded parameter keys, and the values are arbitrary types or arrays. Keys with a null value - are dropped.
        Returns
        The new parameters.

        Generates a URI path using a given URI and a path with checks to + are dropped.

        Returns
        The new parameters.

        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.

        Parameters
        baseUri: string
        URI to use as the base.
        path: string
        Path to append.
        Returns
        Updated URI.

        Appends a URI and query data in a string buffer with special preconditions. + fragment identifiers.

        Parameters
        baseUri: string
        URI to use as the base.
        path: string
        Path to append.
        Returns
        Updated URI.

        Appends a URI and query data in a string buffer with special preconditions. Internal implementation utility, performing very few object allocations.

        Parameters
        buffer: !Array
        A string buffer. The first element must be the base URI, and may have a fragment identifier. If the array contains more than one element, the second element must be an ampersand, and may be overwritten, depending on the base URI. Undefined elements - are treated as empty-string.
        Returns
        The concatenated URI and query data.

        Asserts that there are no fragment or query identifiers, only in uncompiled + are treated as empty-string.Returns

        The concatenated URI and query data.

        Asserts that there are no fragment or query identifiers, only in uncompiled mode.

        Parameters
        uri: string
        The URI to examine.
        code »goog.uri.utils.buildFromEncodedParts ( opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_queryData, opt_fragment )string

        Builds a URI string from already-encoded parts. No encoding is performed. Any component may be omitted as either null or undefined.

        Parameters
        opt_scheme: ?string=
        The scheme such as 'http'.
        opt_userInfo: ?string=
        The user name before the '@'.
        opt_domain: ?string=
        The domain such as 'www.google.com', already URI-encoded.
        opt_port: (string|number|null)=
        The port number.
        opt_path: ?string=
        The path, already URI-encoded. If it is not - empty, it must begin with a slash.
        opt_queryData: ?string=
        The URI-encoded query data.
        opt_fragment: ?string=
        The URI-encoded fragment identifier.
        Returns
        The fully combined URI.
        code »goog.uri.utils.buildQueryData ( keysAndValues, opt_startIndex )string

        Builds a query data string from a sequence of alternating keys and values. + empty, it must begin with a slash.

        opt_queryData: ?string=
        The URI-encoded query data.
        opt_fragment: ?string=
        The URI-encoded fragment identifier.Returns
        The fully combined URI.
        code »goog.uri.utils.buildQueryData ( keysAndValues, opt_startIndex )string

        Builds a query data string from a sequence of alternating keys and values. Currently generates "&key&" for empty args.

        Parameters
        keysAndValues: goog.uri.utils.QueryArray
        Alternating keys and - values. See the typedef.
        opt_startIndex: number=
        A start offset into the arary, defaults to 0.
        Returns
        The encoded query string, in the for 'a=1&b=2'.

        Builds a buffer of query data from a map.

        Parameters
        buffer: !Array
        A string buffer to append to. The + values. See the typedef.
        opt_startIndex: number=
        A start offset into the arary, defaults to 0.
        Returns
        The encoded query string, in the form 'a=1&b=2'.

        Builds a buffer of query data from a map.

        Parameters
        buffer: !Array
        A string buffer to append to. The first element appended will be an '&', and may be replaced by the caller.
        map: Object.<goog.uri.utils.QueryValue>
        An object where keys are URI-encoded parameter keys, and the values conform to the contract - specified in the goog.uri.utils.QueryValue typedef.
        Returns
        The buffer argument.
        code »goog.uri.utils.buildQueryDataBuffer_ ( buffer, keysAndValues, opt_startIndex )!Array

        Builds a buffer of query data from a sequence of alternating keys and values.

        Parameters
        buffer: !Array
        A string buffer to append to. The + specified in the goog.uri.utils.QueryValue typedef.
        Returns
        The buffer argument.
        code »goog.uri.utils.buildQueryDataBuffer_ ( buffer, keysAndValues, opt_startIndex )!Array

        Builds a buffer of query data from a sequence of alternating keys and values.

        Parameters
        buffer: !Array
        A string buffer to append to. The first element appended will be an '&', and may be replaced by the caller.
        keysAndValues: (goog.uri.utils.QueryArray|Arguments)
        An array with - alternating keys and values -- see the typedef.
        opt_startIndex: number=
        A start offset into the arary, defaults to 0.
        Returns
        The buffer argument.

        Builds a query data string from a map. + alternating keys and values -- see the typedef.

        opt_startIndex: number=
        A start offset into the arary, defaults to 0.Returns
        The buffer argument.

        Builds a query data string from a map. Currently generates "&key&" for empty args.

        Parameters
        map: Object
        An object where keys are URI-encoded parameter keys, and the values are arbitrary types or arrays. Keys with a null value - are dropped.
        Returns
        The encoded query string, in the for 'a=1&b=2'.
        Parameters
        uri: ?string
        A possibly null string.
        Returns
        The string URI-decoded, or null if uri is null.
        code »goog.uri.utils.findParam_ ( uri, startIndex, keyEncoded, hashOrEndIndex )number

        Finds the next instance of a query parameter with the specified name. + are dropped.Returns

        The encoded query string, in the form 'a=1&b=2'.
        code »goog.uri.utils.decodeIfPossible_ ( uri, opt_preserveReserved )?string
        Parameters
        uri: ?string
        A possibly null string.
        opt_preserveReserved: boolean=
        If true, percent-encoding of RFC-3986 + reserved characters will not be removed.
        Returns
        The string URI-decoded, or null if uri is null.
        code »goog.uri.utils.findParam_ ( uri, startIndex, keyEncoded, hashOrEndIndex )number

        Finds the next instance of a query parameter with the specified name. Does not instantiate any objects.

        Parameters
        uri: string
        The URI to search. May contain a fragment identifier if opt_hashIndex is specified.
        startIndex: number
        The index to begin searching for the key at. A match may be found even if this is one character after the ampersand.
        keyEncoded: string
        The URI-encoded key.
        hashOrEndIndex: number
        Index to stop looking at. If a hash mark is present, it should be its index, otherwise it should be the length of the string.
        Returns
        The position of the first character in the key's name, - immediately after either a question mark or a dot.

        Gets a URI component by index. + immediately after either a question mark or a dot.

        Gets a URI component by index. It is preferred to use the getPathEncoded() variety of functions ahead, since they are more readable.

        Parameters
        componentIndex: goog.uri.utils.ComponentIndex
        The component index.
        uri: string
        The URI to examine.
        Returns
        The still-encoded component, or null if the component - is not present.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded domain, or null if none.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The domain name still encoded, or null if none.

        Gets the effective scheme for the URL. If the URL is relative then the - scheme is derived from the page's location.

        Parameters
        uri: string
        The URI to examine.
        Returns
        The protocol or scheme, always lower case.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded fragment identifier, or null if none. Does - not include the hash mark.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The fragment identifier, or null if none. Does not - include the hash mark itself.

        Extracts everything up to the port of the URI.

        Parameters
        uri: string
        The URI string.
        Returns
        Everything up to and including the port.

        Gets the first value of a query parameter.

        Parameters
        uri: string
        The URI to process. May contain a fragment.
        keyEncoded: string
        The URI-encoded key. Case-sensitive.
        Returns
        The first value of the parameter (URI-decoded), or null - if the parameter is not found.

        Gets all values of a query parameter.

        Parameters
        uri: string
        The URI to process. May contain a framgnet.
        keyEncoded: string
        The URI-encoded key. Case-snsitive.
        Returns
        All URI-decoded values with the given key. - If the key is not found, this will have length 0, but never be null.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded path, or null if none. Includes the leading - slash, if any.

        Extracts the path of the URL and everything after.

        Parameters
        uri: string
        The URI string.
        Returns
        The URI, starting at the path and including the query - parameters and fragment identifier.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The path still encoded, or null if none. Includes the - leading slash, if any.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The port number, or null if none.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The query data still encoded, or null if none. Does not - include the question mark itself.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The protocol or scheme, or null if none. Does not - include trailing colons or slashes.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded user info, or null if none.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The user name still encoded, or null if none.
        code »goog.uri.utils.hasParam ( uri, keyEncoded )boolean

        Determines if the URI contains a specific key. + is not present.

        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded domain, or null if none.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The domain name still encoded, or null if none.

        Gets the effective scheme for the URL. If the URL is relative then the + scheme is derived from the page's location.

        Parameters
        uri: string
        The URI to examine.
        Returns
        The protocol or scheme, always lower case.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded fragment identifier, or null if none. Does + not include the hash mark.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The fragment identifier, or null if none. Does not + include the hash mark itself.

        Extracts everything up to the port of the URI.

        Parameters
        uri: string
        The URI string.
        Returns
        Everything up to and including the port.

        Gets the first value of a query parameter.

        Parameters
        uri: string
        The URI to process. May contain a fragment.
        keyEncoded: string
        The URI-encoded key. Case-sensitive.
        Returns
        The first value of the parameter (URI-decoded), or null + if the parameter is not found.

        Gets all values of a query parameter.

        Parameters
        uri: string
        The URI to process. May contain a framgnet.
        keyEncoded: string
        The URI-encoded key. Case-snsitive.
        Returns
        All URI-decoded values with the given key. + If the key is not found, this will have length 0, but never be null.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded path, or null if none. Includes the leading + slash, if any.

        Extracts the path of the URL and everything after.

        Parameters
        uri: string
        The URI string.
        Returns
        The URI, starting at the path and including the query + parameters and fragment identifier.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The path still encoded, or null if none. Includes the + leading slash, if any.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The port number, or null if none.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The query data still encoded, or null if none. Does not + include the question mark itself.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The protocol or scheme, or null if none. Does not + include trailing colons or slashes.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The decoded user info, or null if none.
        Parameters
        uri: string
        The URI to examine.
        Returns
        The user name still encoded, or null if none.
        code »goog.uri.utils.hasParam ( uri, keyEncoded )boolean

        Determines if the URI contains a specific key. Performs no object instantiations.

        Parameters
        uri: string
        The URI to process. May contain a fragment - identifier.
        keyEncoded: string
        The URI-encoded key. Case-sensitive.
        Returns
        Whether the key is present.

        Ensures that two URI's have the exact same domain, scheme, and port. + identifier.

        keyEncoded: string
        The URI-encoded key. Case-sensitive.Returns
        Whether the key is present.

        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.

        Parameters
        uri1: string
        The first URI.
        uri2: string
        The second URI.
        Returns
        Whether they have the same domain and port.

        Sets the zx parameter of a URI to a random value.

        Parameters
        uri: string
        Any URI.
        Returns
        That URI with the "zx" parameter added or replaced to - contain a random string.

        Check to see if the user is being phished.

        Gets the URI with the fragment identifier removed.

        Parameters
        uri: string
        The URI to examine.
        Returns
        Everything preceding the hash mark.

        Removes all instances of a query parameter.

        Parameters
        uri: string
        The URI to process. Must not contain a fragment.
        keyEncoded: string
        The URI-encoded key.
        Returns
        The URI with all instances of the parameter removed.
        Parameters
        uri: string
        The URI to examine.
        fragment: ?string
        The encoded fragment identifier, or null if none. - Does not include the hash mark itself.
        Returns
        The URI with the fragment set.
        code »goog.uri.utils.setParam ( uri, keyEncoded, value )string

        Replaces all existing definitions of a parameter with a single definition. + suitable for checking against the browser's same-origin policy.

        Parameters
        uri1: string
        The first URI.
        uri2: string
        The second URI.
        Returns
        Whether they have the same scheme, domain and port.

        Sets the zx parameter of a URI to a random value.

        Parameters
        uri: string
        Any URI.
        Returns
        That URI with the "zx" parameter added or replaced to + contain a random string.

        Check to see if the user is being phished.

        Gets the URI with the fragment identifier removed.

        Parameters
        uri: string
        The URI to examine.
        Returns
        Everything preceding the hash mark.

        Removes all instances of a query parameter.

        Parameters
        uri: string
        The URI to process. Must not contain a fragment.
        keyEncoded: string
        The URI-encoded key.
        Returns
        The URI with all instances of the parameter removed.
        Parameters
        uri: string
        The URI to examine.
        fragment: ?string
        The encoded fragment identifier, or null if none. + Does not include the hash mark itself.
        Returns
        The URI with the fragment set.
        code »goog.uri.utils.setParam ( uri, keyEncoded, value )string

        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.

        Parameters
        uri: string
        The original URI, which may already have query data.
        keyEncoded: string
        The key, which must already be URI encoded.
        value: *
        The value, which will be stringized and encoded (assumed - not already to be encoded).
        Returns
        The URI with the query parameter added.

        Splits a URI into its component parts. + not already to be encoded).Returns

        The URI with the query parameter added.

        Replaces the path.

        Parameters
        uri: string
        URI to use as the base.
        path: string
        New path.
        Returns
        Updated URI.

        Splits a URI into its component parts. Each component can be accessed via the component indices; for example:

        @@ -115,7 +116,7 @@
              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

        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., + arbitrary strings may still look like path names.

        Global Properties

        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/ @@ -189,4 +190,4 @@ $5 = /pub/ietf/uri/ path $6 = query without ? $7 = Related fragment without # -

        Regexp to find trailing question marks and ampersands.

        \ No newline at end of file +

        Regexp to find trailing question marks and ampersands.

        \ No newline at end of file diff --git a/docs/namespace_goog_userAgent.html b/docs/namespace_goog_userAgent.html index 6630a3f..f36462f 100644 --- a/docs/namespace_goog_userAgent.html +++ b/docs/namespace_goog_userAgent.html @@ -1,38 +1,35 @@ -goog.userAgent

        Namespace goog.userAgent

        code »
        Show:

        Global Functions

        Deprecated: Use goog.string.compareVersions.

        Compares two version numbers.

        Parameters
        v1: string
        Version of first item.
        v2: string
        Version of second item.
        Returns
        1 if first argument is higher +goog.userAgent

        Namespace goog.userAgent

        code »
        Show:

        Global Functions

        Deprecated: Use goog.string.compareVersions.

        Compares two version numbers.

        Parameters
        v1: string
        Version of first item.
        v2: string
        Version of second item.
        Returns
        1 if first argument is higher 0 if arguments are equal - -1 if second argument is higher.
        Returns
        the platform (operating system) the user agent is running + -1 if second argument is higher.
        Returns
        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 for the current browser. - Some user agents (I'm thinking of you, Gears WorkerPool) do not expose a - navigator object off the global scope. In that case we return null.

        Returns
        The userAgent string or null if there is none.

        Initialize the goog.userAgent constants that define which platform the user - agent is running on.

        Initializer for goog.userAgent. - - This is a named function so that it can be stripped via the jscompiler - option for stripping types.

        Deprecated alias to goog.userAgent.isDocumentModeOrHigher.

        Parameters
        version: number
        The version to check.
        Returns
        Whether the IE effective document mode is higher or the - same as the given version.

        Whether the IE effective document mode is higher or the same as the given + (on Rhino, for example).

        Returns
        The string that describes the version number of the user + agent.
        Returns
        Returns the document mode (for testing).

        TODO(nnaze): Change type to "Navigator" and update compilation targets.

        Returns
        The native navigator object.

        Returns the userAgent string for the current browser.

        Returns
        The userAgent string.

        Initialize the goog.userAgent constants that define which platform the user + agent is running on.

        Deprecated: Use goog.userAgent.isDocumentModeOrHigher().

        Deprecated alias to goog.userAgent.isDocumentModeOrHigher.

        Parameters
        version: number
        The version to check.
        Returns
        Whether the IE effective document mode is higher or the + same as the given version.

        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.

        Parameters
        documentMode: number
        The document mode version to check.
        Returns
        Whether the IE effective document mode is higher or the - same as the given version.
        Deprecated: Use goog.userAgent.isVersionOrHigher().

        Deprecated alias to goog.userAgent.isVersionOrHigher.

        Parameters
        version: (string|number)
        The version to check.
        Returns
        Whether the user agent version is higher or the same as - the given version.

        Whether the user agent version is higher or the same as the given version. + same as the given version.

        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().

        Returns
        Whether the user agent is running on a mobile device.
        Deprecated: Use goog.userAgent.isVersionOrHigher().

        Deprecated alias to goog.userAgent.isVersionOrHigher.

        Parameters
        version: (string|number)
        The version to check.
        Returns
        Whether the user agent version is higher or the same as + the given version.

        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.

        Parameters
        version: (string|number)
        The version to check.
        Returns
        Whether the user agent version is higher or the same as - the given version.

        Global Properties

        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 + the given version.

        Global Properties

        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, Mozilla Firefox, Camino and many more.

        Whether the user agent is Internet Explorer. This includes other browsers - using Trident as its rendering engine. For example AOL and Netscape 8

        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.

        Whether the user agent is Opera.

        The platform (operating system) the user agent is running on. Default to + 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).

        Deprecated: Use goog.userAgent.product.SAFARI instead. - TODO(nicksantos): Delete this from goog.userAgent.

        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 string denotes Gecko. Gecko is the rendering - engine used by Mozilla, Mozilla Firefox, Camino and many more.

        Whether the user agent is running on an iPad.

        Whether the user agent is running on an iPhone.

        Whether the user agent string denotes Internet Explorer. This includes - other browsers using Trident as its rendering engine. For example AOL - and Netscape 8

        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 string denotes a mobile device.

        Whether the user agent string denotes Opera.

        Whether the user agent string denotes 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.

        Deprecated: Use goog.userAgent.product.SAFARI instead. + TODO(nicksantos): Delete this from goog.userAgent.

        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 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

        \ No newline at end of file + version number is unlikely to change during a session, we cache the results.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_goog_userAgent_product.html b/docs/namespace_goog_userAgent_product.html index f06d154..4ad8688 100644 --- a/docs/namespace_goog_userAgent_product.html +++ b/docs/namespace_goog_userAgent_product.html @@ -6,4 +6,4 @@ http://developer.yahoo.com/yui/articles/gbs/

        Whether the user agent product version is higher or the same as the given version.

        Parameters
        version: (string|number)
        The version to check.
        Returns
        Whether the user agent product version is higher or the same as the given version.

        Global Properties

        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.

        Compiler Constants

        \ No newline at end of file + '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.

        Compiler Constants

        \ No newline at end of file diff --git a/docs/namespace_webdriver.html b/docs/namespace_webdriver.html index c4413ef..562a2c6 100644 --- a/docs/namespace_webdriver.html +++ b/docs/namespace_webdriver.html @@ -1,4 +1,30 @@ -webdriver

        Namespace webdriver

        code »

        Interfaces

        webdriver.CommandExecutor
        Handles the execution of webdriver.Command objects.

        Classes

        webdriver.AbstractBuilder
        Creates new webdriver.WebDriver clients.
        webdriver.ActionSequence
        Class for defining sequences of complex user interactions.
        webdriver.Alert
        Represents a modal dialog such as alert, confirm, or - prompt.
        webdriver.Builder
        No Description.
        webdriver.Capabilities
        No Description.
        webdriver.Command
        Describes a command to be executed by the WebDriverJS framework.
        webdriver.EventEmitter
        Object that can emit events for others to listen for.
        webdriver.FirefoxDomExecutor
        No Description.
        webdriver.Locator
        An element locator.
        webdriver.Session
        Contains information about a WebDriver session.
        webdriver.UnhandledAlertError
        An error returned to indicate that there is an unhandled modal dialog on the - current page.
        webdriver.WebDriver
        Creates a new WebDriver client, which provides control over a browser.
        webdriver.WebElement
        Represents a DOM element.

        Enumerations

        webdriver.Browser
        Recognized browser names.
        webdriver.Button
        Enumeration of the buttons used in the advanced interactions API.
        webdriver.Capability
        Common webdriver capability keys.
        webdriver.CommandName
        Enumeration of predefined names command names that all command processors - will support.
        webdriver.Key
        Representations of pressable keys that aren't text.
        Show:
        \ No newline at end of file +webdriver

        namespace webdriver

        Types

        ActionSequence

        Class for defining sequences of complex user interactions.

        +
        Alert

        Represents a modal dialog such as alert, confirm, or +prompt.

        +
        AlertPromise

        AlertPromise is a promise that will be fulfilled with an Alert.

        +
        Browser

        Recognized browser names.

        +
        Button

        Enumeration of the buttons used in the advanced interactions API.

        +
        Capabilities

        No description.

        +
        Capability

        Common webdriver capability keys.

        +
        Command

        Describes a command to be executed by the WebDriverJS framework.

        +
        CommandExecutor

        Handles the execution of WebDriver commands.

        +
        CommandName

        Enumeration of predefined names command names that all command processors +will support.

        +
        EventEmitter

        Object that can emit events for others to listen for.

        +
        FileDetector

        Used with WebElement#sendKeys on file +input elements (<input type="file">) to detect when the entered key +sequence defines the path to a file.

        +
        Key

        Representations of pressable keys that aren't text.

        +
        Locator

        An element locator.

        +
        Serializable

        Defines an object that can be asynchronously serialized to its WebDriver +wire representation.

        +
        Session

        Contains information about a WebDriver session.

        +
        TouchSequence

        Class for defining sequences of user touch interactions.

        +
        UnhandledAlertError

        An error returned to indicate that there is an unhandled modal dialog on the +current page.

        +
        WebDriver

        Creates a new WebDriver client, which provides control over a browser.

        +
        WebElement

        Represents a DOM element.

        +
        WebElementPromise

        WebElementPromise is a promise that will be fulfilled with a WebElement.

        +

        Type Definitions

        webdriver.ProxyConfig{proxyType: string}

        Describes how a proxy should be configured for a WebDriver session. +Proxy configuration object, as defined by the WebDriver wire protocol.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_By.html b/docs/namespace_webdriver_By.html index 342af00..dbadc13 100644 --- a/docs/namespace_webdriver_By.html +++ b/docs/namespace_webdriver_By.html @@ -1,26 +1,56 @@ -webdriver.By

        Namespace webdriver.By

        code »

        A collection of factory functions for creating webdriver.Locator - instances.

        Show:

        Type Definitions

        code »webdriver.By.Hash : ({className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})
        Short-hand expressions for the primary element locator strategies. - For example the following two statements are equivalent: -
        - var e1 = driver.findElement(webdriver.By.id('foo'));
        - var e2 = driver.findElement({id: 'foo'});
        - 
        - -

        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.

        Global Functions

        Locates elements that have a specific class name. The returned locator - is equivalent to searching for elements with the CSS selector ".clazz".

        Parameters
        className: string
        The class name to search for.
        Returns
        The new locator.

        Locates elements using a CSS selector. For browsers that do not support - CSS selectors, WebDriver implementations may return an - invalid selector error. An - implementation may, however, emulate the CSS selector API.

        Parameters
        selector: string
        The CSS selector to use.
        Returns
        The new locator.

        Locates an element by its ID.

        Parameters
        id: string
        The ID to search for.
        Returns
        The new locator.

        Locates an elements by evaluating a - JavaScript expression. - The result of this expression must be an element or list of elements.

        Parameters
        script: !(string|Function)
        The script to execute.
        var_args: ...*
        The arguments to pass to the script.
        Returns
        A new, - JavaScript-based locator function.

        Locates link elements whose visible - text matches the given string.

        Parameters
        text: string
        The link text to search for.
        Returns
        The new locator.

        Locates elements whose name attribute has the given value.

        Parameters
        name: string
        The name attribute to search for.
        Returns
        The new locator.

        Locates link elements whose visible - text contains the given substring.

        Parameters
        text: string
        The substring to check for in a link's visible text.
        Returns
        The new locator.

        Locates elements with a given tag name. The returned locator is - equivalent to using the getElementsByTagName DOM function.

        Parameters
        text: string
        The substring to check for in a link's visible text.
        Returns
        The new locator.

        Locates elements matching a XPath selector. Care should be taken when - using an XPath selector with a webdriver.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
        xpath: string
        The XPath selector to use.
        Returns
        The new locator.
        \ No newline at end of file +webdriver.By

        namespace webdriver.By

        A collection of factory functions for creating webdriver.Locator +instances.

        +

        Functions

        className(className)code »

        Locates elements that have a specific class name. The returned locator +is equivalent to searching for elements with the CSS selector ".clazz".

        +
        Parameters
        classNamestring

        The class name to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        css(selector)code »

        Locates elements using a CSS selector. For browsers that do not support +CSS selectors, WebDriver implementations may return an +invalid selector error. An +implementation may, however, emulate the CSS selector API.

        +
        Parameters
        selectorstring

        The CSS selector to use.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        id(id)code »

        Locates an element by its ID.

        +
        Parameters
        idstring

        The ID to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        js(script, var_args)code »

        Locates an elements by evaluating a +JavaScript expression. +The result of this expression must be an element or list of elements.

        +
        Parameters
        script(string|Function)

        The script to execute.

        +
        var_args...*

        The arguments to pass to the script.

        +
        Returns
        function(webdriver.WebDriver): webdriver.promise.Promise

        A new, +JavaScript-based locator function.

        +

        linkText(text)code »

        Locates link elements whose visible +text matches the given string.

        +
        Parameters
        textstring

        The link text to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        name(name)code »

        Locates elements whose name attribute has the given value.

        +
        Parameters
        namestring

        The name attribute to search for.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        partialLinkText(text)code »

        Locates link elements whose visible +text contains the given substring.

        +
        Parameters
        textstring

        The substring to check for in a link's visible text.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        tagName(text)code »

        Locates elements with a given tag name. The returned locator is +equivalent to using the +getElementsByTagName +DOM function.

        +
        Parameters
        textstring

        The substring to check for in a link's visible text.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        xpath(xpath)code »

        Locates elements matching a XPath selector. Care should be taken when +using an XPath selector with a webdriver.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
        xpathstring

        The XPath selector to use.

        +
        Returns
        webdriver.Locator

        The new locator.

        +

        Type Definitions

        By.Hash({className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        Short-hand expressions for the primary element locator strategies. +For example the following two statements are equivalent:

        +
        var e1 = driver.findElement(webdriver.By.id('foo'));
        +var e2 = driver.findElement({id: 'foo'});
        +
        +

        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.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_http.html b/docs/namespace_webdriver_http.html index 19bb8c3..7cdabfe 100644 --- a/docs/namespace_webdriver_http.html +++ b/docs/namespace_webdriver_http.html @@ -1,4 +1,6 @@ -webdriver.http

        Namespace webdriver.http

        code »

        Interfaces

        webdriver.http.Client
        Interface used for sending individual HTTP requests to the server.

        Classes

        webdriver.http.CorsClient
        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.
        webdriver.http.Executor
        A command executor that communicates with a server using the WebDriver - command protocol.
        webdriver.http.Request
        Describes a partial HTTP request.
        webdriver.http.Response
        Represents a HTTP response.
        webdriver.http.XhrClient
        A HTTP client that sends requests using XMLHttpRequests.
        Show:

        Global Functions

        Converts a headers object to a HTTP header block string.

        Parameters
        headers: !Object.<string>
        The headers object to convert.
        Returns
        The headers as a string.
        \ No newline at end of file +webdriver.http

        namespace webdriver.http

        Types

        Client

        Interface used for sending individual HTTP requests to the server.

        +
        Executor

        A command executor that communicates with a server using the WebDriver +command protocol.

        +
        Request

        Describes a partial HTTP request.

        +
        Response

        Represents a HTTP response.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_logging.html b/docs/namespace_webdriver_logging.html index 2cd469f..34e6fce 100644 --- a/docs/namespace_webdriver_logging.html +++ b/docs/namespace_webdriver_logging.html @@ -1,4 +1,70 @@ -webdriver.logging

        Namespace webdriver.logging

        code »

        Classes

        webdriver.logging.Entry
        A single log entry.

        Enumerations

        webdriver.logging.Level
        Logging levels.
        webdriver.logging.LevelName
        Log level names from WebDriver's JSON wire protocol.
        webdriver.logging.Type
        Common log types.
        Show:

        Type Definitions

        Global Functions

        Converts a level name or value to a webdriver.logging.Level value. - If the name/value is not recognized, webdriver.logging.Level.ALL - will be returned.

        Parameters
        nameOrValue: (number|string)
        The log level name, or value, to - convert .
        Returns
        The converted level.
        \ No newline at end of file +webdriver.logging

        namespace webdriver.logging

        Defines WebDriver's logging system. The logging system is +broken into major components: local and remote logging.

        +

        The local logging API, which is anchored by the +Logger class, is similar to Java's +logging API. Loggers, retrieved by webdriver.logging.getLogger, use +hierarchical, dot-delimited namespaces +(e.g. "" > "webdriver" > "webdriver.logging"). Recorded log messages are +represented by the LogRecord class. You +can capture log records by +attaching a handler function +to the desired logger. For convenience, you can quickly enable logging to +the console by simply calling +webdriver.logging.installConsoleHandler().

        +

        The remote logging API +allows you to retrieve logs from a remote WebDriver server. This API uses the +Preferences class to define desired log levels prior to create a +WebDriver session:

        +
        var prefs = new webdriver.logging.Preferences();
        +prefs.setLevel(webdriver.logging.Type.BROWSER,
        +               webdriver.logging.Level.DEBUG);
        +
        +var caps = webdriver.Capabilities.chrome();
        +caps.setLoggingPrefs(prefs);
        +// ...
        +
        +

        Remote log entries are represented by the Entry class and may be +retrieved via webdriver.WebDriver.Logs:

        +
        driver.manage().logs().get(webdriver.logging.Type.BROWSER)
        +    .then(function(entries) {
        +       entries.forEach(function(entry) {
        +         console.log('[%s] %s', entry.level.name, entry.message);
        +       });
        +    });
        +
        +

        NOTE: Only a few browsers support the remote logging API (notably +Firefox and Chrome). Firefox supports basic logging functionality, while +Chrome exposes robust +performance logging +options. Remote logging is still considered a non-standard feature, and the +APIs exposed by this module for it are non-frozen. Once logging is officially +defined by the W3C WebDriver spec, this +module will be updated to use a consistent API for local and remote logging.

        +

        Functions

        addConsoleHandler(opt_logger)code »

        Adds the console handler to the given logger. The console handler will log +all messages using the JavaScript Console API.

        +
        Parameters
        opt_logger?webdriver.logging.Logger=

        The logger to add the handler to; defaults +to the root logger.

        +

        getLevel(nameOrValue)code »

        Converts a level name or value to a webdriver.logging.Level value. +If the name/value is not recognized, webdriver.logging.Level.ALL +will be returned.

        +
        Parameters
        nameOrValue(number|string)

        The log level name, or value, to +convert .

        +
        Returns
        webdriver.logging.Level

        The converted level.

        +

        getLogger(opt_name)code »

        Finds a named logger.

        +
        Parameters
        opt_namestring=

        The dot-delimited logger name, such as +"webdriver.logging.Logger". Defaults to the name of the root logger.

        +
        Returns
        webdriver.logging.Logger

        The named logger.

        +

        installConsoleHandler()code »

        Installs the console log handler on the root logger.

        +

        removeConsoleHandler(opt_logger)code »

        Removes the console log handler from the given logger.

        +
        Parameters
        opt_logger?webdriver.logging.Logger=

        The logger to remove the handler from; defaults +to the root logger.

        +

        Types

        Entry

        A single log entry recorded by a WebDriver component, such as a remote +WebDriver server.

        +
        Level

        The Level class defines a set of standard logging levels that +can be used to control logging output.

        +
        LogRecord

        LogRecord objects are used to pass logging requests between +the logging framework and individual log Handlers.

        +
        Logger

        The Logger is an object used for logging debug messages.

        +
        Preferences

        Describes the log preferences for a WebDriver session.

        +
        Type

        No description.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_process.html b/docs/namespace_webdriver_process.html index eafcd04..b8d1b2e 100644 --- a/docs/namespace_webdriver_process.html +++ b/docs/namespace_webdriver_process.html @@ -5,4 +5,4 @@ object.

        Sets an environment value. If the new value is either null or undefined, the environment variable will be cleared.

        Parameters
        name: string
        The value to set.
        value: *
        The new value; will be coerced to a string.

        Global Properties

        Whether the current environment is using Node's native process object.

        The global process object to use. Will either be Node's global process object, or an approximation of it for use in a browser - environment.

        \ No newline at end of file + environment. \ No newline at end of file diff --git a/docs/namespace_webdriver_promise.html b/docs/namespace_webdriver_promise.html index e8f69c3..8724e17 100644 --- a/docs/namespace_webdriver_promise.html +++ b/docs/namespace_webdriver_promise.html @@ -1,79 +1,676 @@ -webdriver.promise

        Namespace webdriver.promise

        code »

        Classes

        webdriver.promise.CanceledTaskError_
        Special error used to signal when a task is canceled because a previous - task in the same frame failed.
        webdriver.promise.ControlFlow
        Handles the execution of scheduled tasks, each of which may be an - asynchronous operation.
        webdriver.promise.Deferred
        Represents a value that will be resolved at some point in the future.
        webdriver.promise.Frame_
        An execution frame within a webdriver.promise.ControlFlow.
        webdriver.promise.Node_
        A single node in an webdriver.promise.ControlFlow's task tree.
        webdriver.promise.Promise
        Represents the eventual value of a completed operation.
        webdriver.promise.Task_
        A task to be executed by a webdriver.promise.ControlFlow.
        Show:

        Global Functions

        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.

        Parameters
        arr: !Array
        An array of - promises to wait on.
        Returns
        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.
        code »webdriver.promise.asap ( value, callback, opt_errback )

        Invokes the appropriate callback function as soon as a promised - value is resolved. This function is similar to - webdriver.promise.when, except it does not return a new promise.

        Parameters
        value: *
        The value to observe.
        callback: Function
        The function to call when the value is - resolved successfully.
        opt_errback: Function=
        The function to call when the value is - rejected.

        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.

        Parameters
        fn: !Function
        The function to wrap.
        Returns
        A promise that will be resolved with the - result of the provided function's callback.
        Returns
        The currently active control flow.

        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.

        Parameters
        callback: function(!webdriver.promise.ControlFlow)
        The entry point - to the newly created flow.
        Returns
        A promise that resolves to the callback - result.

        Creates a new deferred object.

        Parameters
        opt_canceller: Function=
        Function to call when cancelling the - computation of this instance's value.
        Returns
        The new deferred object.

        Creates a promise that will be resolved at a set time in the future.

        Parameters
        ms: number
        The amount of time, in milliseconds, to wait before - resolving the promise.
        Returns
        The promise.
        code »<TYPE, SELF> webdriver.promise.filter ( arr, fn, opt_self )

        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.

        Parameters
        arr: !(Array.<TYPE>|webdriver.promise.Promise)
        The - array to iterator over, or a promise that will resolve to said array.
        fn: function(this: SELF, TYPE, number, !Array.<TYPE>): (boolean|webdriver.promise.Promise.<boolean>)
        The function - to call for each element in the array.
        opt_self: SELF=
        The object to be used as the value of 'this' within - fn.

        Creates a promise that has been resolved with the given value.

        Parameters
        opt_value: *=
        The resolved value.
        Returns
        The resolved promise.
        Parameters
        obj: !(Array|Object)
        the object to resolve.
        Returns
        A promise that will be resolved with the - input object once all of its values have been fully resolved.
        Parameters
        value: *
        The value to fully resolve. If a promise, assumed to - already be resolved.
        Returns
        A promise for a fully resolved version - of the input 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 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
        value: *
        The value to fully resolve.
        Returns
        A promise for a fully resolved version - of the input value.

        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.

        Parameters
        value: *
        The value to test.
        Returns
        Whether the value is an error.

        Determines whether a value should be treated as a promise. - Any object whose "then" property is a function will be considered a promise.

        Parameters
        value: *
        The value to test.
        Returns
        Whether the value is a promise.
        code »<TYPE, SELF> webdriver.promise.map ( arr, fn, opt_self )

        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.

        Parameters
        arr: !(Array.<TYPE>|webdriver.promise.Promise)
        The - array to iterator over, or a promise that will resolve to said array.
        fn: function(this: SELF, TYPE, number, !Array.<TYPE>): ?
        The - function to call for each element in the array. This function should - expect three arguments (the element, the index, and the array itself.
        opt_self: SELF=
        The object to be used as the value of 'this' within - fn.

        Creates a promise that has been rejected with the given reason.

        Parameters
        opt_reason: *=
        The rejection reason; may be any value, but is - usually an Error or a string.
        Returns
        The rejected promise.

        Changes the default flow to use when no others are active.

        Parameters
        flow: !webdriver.promise.ControlFlow
        The new default flow.
        Throws
        Error
        If the default flow is not currently active.
        code »webdriver.promise.when ( value, opt_callback, opt_errback )!webdriver.promise.Promise

        Registers an observer on a promised value, 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.

        Parameters
        value: *
        The value to observe.
        opt_callback: Function=
        The function to call when the value is - resolved successfully.
        opt_errback: Function=
        The function to call when the value is - rejected.
        Returns
        A new promise.

        Global Properties

        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.

        \ No newline at end of file +webdriver.promise

        namespace webdriver.promise

        The promise module is centered around the +ControlFlow, a class that +coordinates the execution of asynchronous tasks. The ControlFlow allows users +to focus on the imperative commands for their script without worrying about +chaining together every single asynchronous action, which can be tedious and +verbose. APIs may be layered on top of the control flow to read as if they +were synchronous. For instance, the core +WebDriver API is built on top of the +control flow, allowing users to write

        +
        driver.get('http://www.google.com/ncr');
        +driver.findElement({name: 'q'}).sendKeys('webdriver');
        +driver.findElement({name: 'btnGn'}).click();
        +
        +

        instead of

        +
        driver.get('http://www.google.com/ncr')
        +.then(function() {
        +  return driver.findElement({name: 'q'});
        +})
        +.then(function(q) {
        +  return q.sendKeys('webdriver');
        +})
        +.then(function() {
        +  return driver.findElement({name: 'btnG'});
        +})
        +.then(function(btnG) {
        +  return btnG.click();
        +});
        +
        +

        Tasks and Task Queues

        +

        The control flow is based on the concept of tasks and task queues. Tasks are +functions that define the basic unit of work for the control flow to execute. +Each task is scheduled via +ControlFlow#execute(), which +will return a Promise that will be resolved +with the task's result.

        +

        A task queue contains all of the tasks scheduled within a single turn of the +JavaScript event loop. The control flow will create a new task queue +the first time a task is scheduled within an event loop.

        +
        var flow = promise.controlFlow();
        +flow.execute(foo);       // Creates a new task queue and inserts foo.
        +flow.execute(bar);       // Inserts bar into the same queue as foo.
        +setTimeout(function() {
        +  flow.execute(baz);     // Creates a new task queue and inserts baz.
        +}, 0);
        +
        +

        Whenever the control flow creates a new task queue, it will automatically +begin executing tasks in the next available turn of the event loop. This +execution is scheduled using a "micro-task" timer, such as a (native) +Promise.then() callback.

        +
        setTimeout(() => console.log('a'));
        +Promise.resolve().then(() => console.log('b'));  // A native promise.
        +flow.execute(() => console.log('c'));
        +Promise.resolve().then(() => console.log('d'));
        +setTimeout(() => console.log('fin'));
        +// b
        +// c
        +// d
        +// a
        +// fin
        +
        +

        In the example above, b/c/d is logged before a/fin because native promises +and this module use "micro-task" timers, which have a higher priority than +"macro-tasks" like setTimeout.

        +

        Task Execution

        +

        Upon creating a task queue, and whenever an exisiting queue completes a task, +the control flow will schedule a micro-task timer to process any scheduled +tasks. This ensures no task is ever started within the same turn of the +JavaScript event loop in which it was scheduled, nor is a task ever started +within the same turn that another finishes.

        +

        When the execution timer fires, a single task will be dequeued and executed. +There are several important events that may occur while executing a task +function:

        +
        1. A new task queue is created by a call to +ControlFlow#execute(). Any +tasks scheduled within this task queue are considered subtasks of the +current task.
        2. The task function throws an error. Any scheduled tasks are immediately +discarded and the task's promised result (previously returned by +ControlFlow#execute()) is +immediately rejected with the thrown error.
        3. The task function returns sucessfully.
        +

        If a task function created a new task queue, the control flow will wait for +that queue to complete before processing the task result. If the queue +completes without error, the flow will settle the task's promise with the +value originaly returned by the task function. On the other hand, if the task +queue termintes with an error, the task's promise will be rejected with that +error.

        +
        flow.execute(function() {
        +  flow.execute(() => console.log('a'));
        +  flow.execute(() => console.log('b'));
        +});
        +flow.execute(() => console.log('c'));
        +// a
        +// b
        +// c
        +
        +

        Promise Integration

        +

        In addition to the ControlFlow class, +the promise module also exports a Promise/A+ +implementation that is deeply +integrated with the ControlFlow. First and foremost, each promise +callback is scheduled with the +control flow as a task. As a result, each callback is invoked in its own turn +of the JavaScript event loop with its own task queue. If any tasks are +scheduled within a callback, the callback's promised result will not be +settled until the task queue has completed.

        +
        promise.fulfilled().then(function() {
        +  flow.execute(function() {
        +    console.log('b');
        +  });
        +}).then(() => console.log('a'));
        +// b
        +// a
        +
        +

        Scheduling Promise Callbacks

        +

        How callbacks are scheduled in the control flow depends on when they are +attached to the promise. Callbacks attached to a previously resolved +promise are immediately enqueued as subtasks of the currently running task.

        +
        var p = promise.fulfilled();
        +flow.execute(function() {
        +  flow.execute(() => console.log('A'));
        +  p.then(      () => console.log('B'));
        +  flow.execute(() => console.log('C'));
        +  p.then(      () => console.log('D'));
        +}).then(function() {
        +  console.log('fin');
        +});
        +// A
        +// B
        +// C
        +// D
        +// fin
        +
        +

        When a promise is resolved while a task function is on the call stack, any +callbacks also registered in that stack frame are scheduled as if the promise +were already resolved:

        +
        var d = promise.defer();
        +flow.execute(function() {
        +  flow.execute(  () => console.log('A'));
        +  d.promise.then(() => console.log('B'));
        +  flow.execute(  () => console.log('C'));
        +  d.promise.then(() => console.log('D'));
        +
        +  d.fulfill();
        +}).then(function() {
        +  console.log('fin');
        +});
        +// A
        +// B
        +// C
        +// D
        +// fin
        +
        +

        If a promise is resolved while a task function is on the call stack, any +previously registered callbacks (i.e. attached while the task was not on +the call stack), act as interrupts and are inserted at the front of the +task queue. If multiple promises are fulfilled, their interrupts are enqueued +in the order the promises are resolved.

        +
        var d1 = promise.defer();
        +d1.promise.then(() => console.log('A'));
        +
        +var d2 = promise.defer();
        +d2.promise.then(() => console.log('B'));
        +
        +flow.execute(function() {
        +  flow.execute(() => console.log('C'));
        +  flow.execute(() => console.log('D'));
        +  d1.fulfill();
        +  d2.fulfill();
        +}).then(function() {
        +  console.log('fin');
        +});
        +// A
        +// B
        +// C
        +// D
        +// fin
        +
        +

        Within a task function (or callback), each step of a promise chain acts as +an interrupt on the task queue:

        +
        var d = promise.defer();
        +flow.execute(function() {
        +  d.promise.
        +      then(() => console.log('A')).
        +      then(() => console.log('B')).
        +      then(() => console.log('C')).
        +      then(() => console.log('D'));
        +
        +  flow.execute(() => console.log('E'));
        +  d.fulfill();
        +}).then(function() {
        +  console.log('fin');
        +});
        +// A
        +// B
        +// C
        +// D
        +// E
        +// fin
        +
        +

        If there are multiple promise chains derived from a single promise, they are +processed in the order created:

        +
        var d = promise.defer();
        +flow.execute(function() {
        +  var chain = d.promise.then(() => console.log('A'));
        +
        +  chain.then(() => console.log('B')).
        +      then(() => console.log('C'));
        +
        +  chain.then(() => console.log('D')).
        +      then(() => console.log('E'));
        +
        +  flow.execute(() => console.log('F'));
        +
        +  d.fulfill();
        +}).then(function() {
        +  console.log('fin');
        +});
        +// A
        +// B
        +// C
        +// D
        +// E
        +// F
        +// fin
        +
        +

        Even though a subtask's promised result will never resolve while the task +function is on the stack, it will be treated as a promise resolved within the +task. In all other scenarios, a task's promise behaves just like a normal +promise. In the sample below, C/D is loggged before B because the +resolution of subtask1 interrupts the flow of the enclosing task. Within +the final subtask, E/F is logged in order because subtask1 is a resolved +promise when that task runs.

        +
        flow.execute(function() {
        +  var subtask1 = flow.execute(() => console.log('A'));
        +  var subtask2 = flow.execute(() => console.log('B'));
        +
        +  subtask1.then(() => console.log('C'));
        +  subtask1.then(() => console.log('D'));
        +
        +  flow.execute(function() {
        +    flow.execute(() => console.log('E'));
        +    subtask1.then(() => console.log('F'));
        +  });
        +}).then(function() {
        +  console.log('fin');
        +});
        +// A
        +// C
        +// D
        +// B
        +// E
        +// F
        +// fin
        +
        +

        Note: while the ControlFlow will wait for +tasks and +callbacks to complete, it +will not wait for unresolved promises created within a task:

        +
        flow.execute(function() {
        +  var p = new promise.Promise(function(fulfill) {
        +    setTimeout(fulfill, 100);
        +  });
        +
        +  p.then(() => console.log('promise resolved!'));
        +
        +}).then(function() {
        +  console.log('task complete!');
        +});
        +// task complete!
        +// promise resolved!
        +
        +

        Finally, consider the following:

        +
        var d = promise.defer();
        +d.promise.then(() => console.log('A'));
        +d.promise.then(() => console.log('B'));
        +
        +flow.execute(function() {
        +  flow.execute(  () => console.log('C'));
        +  d.promise.then(() => console.log('D'));
        +
        +  flow.execute(  () => console.log('E'));
        +  d.promise.then(() => console.log('F'));
        +
        +  d.fulfill();
        +
        +  flow.execute(  () => console.log('G'));
        +  d.promise.then(() => console.log('H'));
        +}).then(function() {
        +  console.log('fin');
        +});
        +// A
        +// B
        +// C
        +// D
        +// E
        +// F
        +// G
        +// H
        +// fin
        +
        +

        In this example, callbacks are registered on d.promise both before and +during the invocation of the task function. When d.fulfill() is called, +the callbacks registered before the task (A & B) are registered as +interrupts. The remaining callbacks were all attached within the task and +are scheduled in the flow as standard tasks.

        +

        Generator Support

        +

        Generators may be scheduled as tasks within a control flow or attached +as callbacks to a promise. Each time the generator yields a promise, the +control flow will wait for that promise to settle before executing the next +iteration of the generator. The yielded promise's fulfilled value will be +passed back into the generator:

        +
        flow.execute(function* () {
        +  var d = promise.defer();
        +
        +  setTimeout(() => console.log('...waiting...'), 25);
        +  setTimeout(() => d.fulfill(123), 50);
        +
        +  console.log('start: ' + Date.now());
        +
        +  var value = yield d.promise;
        +  console.log('mid: %d; value = %d', Date.now(), value);
        +
        +  yield promise.delayed(10);
        +  console.log('end: ' + Date.now());
        +}).then(function() {
        +  console.log('fin');
        +});
        +// start: 0
        +// ...waiting...
        +// mid: 50; value = 123
        +// end: 60
        +// fin
        +
        +

        Yielding the result of a promise chain will wait for the entire chain to +complete:

        +
        promise.fulfilled().then(function* () {
        +  console.log('start: ' + Date.now());
        +
        +  var value = yield flow.
        +      execute(() => console.log('A')).
        +      then(   () => console.log('B')).
        +      then(   () => 123);
        +
        +  console.log('mid: %s; value = %d', Date.now(), value);
        +
        +  yield flow.execute(() => console.log('C'));
        +}).then(function() {
        +  console.log('fin');
        +});
        +// start: 0
        +// A
        +// B
        +// mid: 2; value = 123
        +// C
        +// fin
        +
        +

        Yielding a rejected promise will cause the rejected value to be thrown +within the generator function:

        +
        flow.execute(function* () {
        +  console.log('start: ' + Date.now());
        +  try {
        +    yield promise.delayed(10).then(function() {
        +      throw Error('boom');
        +    });
        +  } catch (ex) {
        +    console.log('caught time: ' + Date.now());
        +    console.log(ex.message);
        +  }
        +});
        +// start: 0
        +// caught time: 10
        +// boom
        +
        +

        Error Handling

        +

        ES6 promises do not require users to handle a promise rejections. This can +result in subtle bugs as the rejections are silently "swallowed" by the +Promise class.

        +
        Promise.reject(Error('boom'));
        +// ... *crickets* ...
        +
        +

        Selenium's promise module, on the other hand, +requires that every rejection be explicitly handled. When a +Promise is rejected and no callbacks +are defined on that promise, it is considered an unhandled rejection and +reproted to the active task queue. If the rejection remains unhandled after +a single turn of the event loop (scheduled with a micro-task), it +will propagate up the stack.

        +

        Error Propagation

        +

        If an unhandled rejection occurs within a task function, that task's promised +result is rejected and all remaining subtasks are discarded:

        +
        flow.execute(function() {
        +  // No callbacks registered on promise -> unhandled rejection
        +  promise.rejected(Error('boom'));
        +  flow.execute(function() { console.log('this will never run'); });
        +}).thenCatch(function(e) {
        +  console.log(e.message);
        +});
        +// boom
        +
        +

        The promised results for discarded tasks are silently rejected with a +cancellation error and existing callback chains will never fire.

        +
        flow.execute(function() {
        +  promise.rejected(Error('boom'));
        +  flow.execute(function() { console.log('a'); }).
        +      then(function() { console.log('b'); });
        +}).thenCatch(function(e) {
        +  console.log(e.message);
        +});
        +// boom
        +
        +

        An unhandled rejection takes precedence over a task function's returned +result, even if that value is another promise:

        +
        flow.execute(function() {
        +  promise.rejected(Error('boom'));
        +  return flow.execute(someOtherTask);
        +}).thenCatch(function(e) {
        +  console.log(e.message);
        +});
        +// boom
        +
        +

        If there are multiple unhandled rejections within a task, they are packaged +in a MultipleUnhandledRejectionError, which has an errors property that is a +Set of the recorded unhandled rejections:

        +
        flow.execute(function() {
        +  promise.rejected(Error('boom1'));
        +  promise.rejected(Error('boom2'));
        +}).thenCatch(function(ex) {
        +  console.log(ex instanceof promise.MultipleUnhandledRejectionError);
        +  for (var e of ex.errors) {
        +    console.log(e.message);
        +  }
        +});
        +// boom1
        +// boom2
        +
        +

        When a subtask is discarded due to an unreported rejection in its parent +frame, the existing callbacks on that task will never settle and the +callbacks will not be invoked. If a new callback is attached ot the subtask +after it has been discarded, it is handled the same as adding a callback +to a cancelled promise: the error-callback path is invoked. This behavior is +intended to handle cases where the user saves a reference to a task promise, +as illustrated below.

        +
        var subTask;
        +flow.execute(function() {
        +  promise.rejected(Error('boom'));
        +  subTask = flow.execute(function() {});
        +}).thenCatch(function(e) {
        +  console.log(e.message);
        +}).then(function() {
        +  return subTask.then(
        +      () => console.log('subtask success!'),
        +      (e) => console.log('subtask failed:\n' + e));
        +});
        +// boom
        +// subtask failed:
        +// DiscardedTaskError: Task was discarded due to a previous failure: boom
        +
        +

        When a subtask fails, its promised result is treated the same as any other +promise: it must be handled within one turn of the rejection or the unhandled +rejection is propagated to the parent task. This means users can catch errors +from complex flows from the top level task:

        +
        flow.execute(function() {
        +  flow.execute(function() {
        +    flow.execute(function() {
        +      throw Error('fail!');
        +    });
        +  });
        +}).thenCatch(function(e) {
        +  console.log(e.message);
        +});
        +// fail!
        +
        +

        Unhandled Rejection Events

        +

        When an unhandled rejection propagates to the root of the control flow, the +flow will emit an uncaughtException event. If no listeners are registered +on the flow, the error will be rethrown to the global error handler: an +uncaughtException event from the +process object in node, or +window.onerror when running in a browser.

        +

        Bottom line: you must handle rejected promises.

        +

        Promise/A+ Compatibility

        +

        This promise module is compliant with the Promise/A+ specification +except for sections 2.2.6.1 and 2.2.6.2:

        +
        +
        • then may be called multiple times on the same promise. +
          • If/when promise is fulfilled, all respective onFulfilled callbacks +must execute in the order of their originating calls to then.
          • If/when promise is rejected, all respective onRejected callbacks +must execute in the order of their originating calls to then.
          +
        +
        +

        Specifically, the conformance tests contains the following scenario (for +brevity, only the fulfillment version is shown):

        +
        var p1 = Promise.resolve();
        +p1.then(function() {
        +  console.log('A');
        +  p1.then(() => console.log('B'));
        +});
        +p1.then(() => console.log('C'));
        +// A
        +// C
        +// B
        +
        +

        Since the ControlFlow executes promise callbacks as +tasks, with this module, the result would be

        +
        var p2 = promise.fulfilled();
        +p2.then(function() {
        +  console.log('A');
        +  p2.then(() => console.log('B');
        +});
        +p2.then(() => console.log('C'));
        +// A
        +// B
        +// C
        +
        +

        Functions

        <T> all(arr)code »

        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.

        +
        Parameters
        arrArray<(T|webdriver.promise.Promise<T>)>

        An array of +promises to wait on.

        +
        Returns
        webdriver.promise.Promise<Array<T>>

        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.

        +

        asap(value, callback, opt_errback)code »

        Invokes the appropriate callback function as soon as a promised +value is resolved. This function is similar to +webdriver.promise.when, except it does not return a new promise.

        +
        Parameters
        value*

        The value to observe.

        +
        callbackFunction

        The function to call when the value is +resolved successfully.

        +
        opt_errback?Function=

        The function to call when the value is +rejected.

        +

        captureStackTrace(name, msg, topFn)code »

        Generates an error to capture the current stack trace.

        +
        Parameters
        namestring

        Error name for this stack trace.

        +
        msgstring

        Message to record.

        +
        topFnFunction

        The function that should appear at the top of the +stack; only applicable in V8.

        +
        Returns
        Error

        The generated error.

        +

        checkedNodeCall(fn, var_args)code »

        Wraps a function that expects a node-style callback as its final +argument. This callback expects two arguments: an error value (which will be +null if the call succeeded), and the success value as the second argument. +The callback will the resolve or reject the returned promise, based on its arguments.

        +
        Parameters
        fnFunction

        The function to wrap.

        +
        var_args...?

        The arguments to apply to the function, excluding the +final callback.

        +
        Returns
        webdriver.promise.Promise

        A promise that will be resolved with the +result of the provided function's callback.

        +

        consume(generatorFn, opt_self, var_args)code »

        Consumes a GeneratorFunction. Each time the generator yields a +promise, this function will wait for it to be fulfilled before feeding the +fulfilled value back into next. Likewise, if a yielded promise is +rejected, the rejection error will be passed to throw.

        +

        Example 1: the Fibonacci Sequence.

        +
        promise.consume(function* fibonacci() {
        +  var n1 = 1, n2 = 1;
        +  for (var i = 0; i < 4; ++i) {
        +    var tmp = yield n1 + n2;
        +    n1 = n2;
        +    n2 = tmp;
        +  }
        +  return n1 + n2;
        +}).then(function(result) {
        +  console.log(result);  // 13
        +});
        +
        +

        Example 2: a generator that throws.

        +
        promise.consume(function* () {
        +  yield promise.delayed(250).then(function() {
        +    throw Error('boom');
        +  });
        +}).thenCatch(function(e) {
        +  console.log(e.toString());  // Error: boom
        +});
        +
        +
        Parameters
        generatorFnFunction

        The generator function to execute.

        +
        opt_self?Object=

        The object to use as "this" when invoking the +initial generator.

        +
        var_args...*

        Any arguments to pass to the initial generator.

        +
        Returns
        webdriver.promise.Promise<?>

        A promise that will resolve to the +generator's final result.

        +
        Throws
        TypeError

        If the given function is not a generator.

        +

        controlFlow()code »

        Returns
        webdriver.promise.ControlFlow

        The currently active control flow.

        +

        createFlow(callback)code »

        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.

        +
        Parameters
        callbackfunction(webdriver.promise.ControlFlow): ?

        The entry point +to the newly created flow.

        +
        Returns
        webdriver.promise.Promise

        A promise that resolves to the callback +result.

        +

        <T> defer()code »

        Creates a new deferred object.

        +
        Returns
        webdriver.promise.Deferred<T>

        The new deferred object.

        +

        delayed(ms)code »

        Creates a promise that will be resolved at a set time in the future.

        +
        Parameters
        msnumber

        The amount of time, in milliseconds, to wait before +resolving the promise.

        +
        Returns
        webdriver.promise.Promise

        The promise.

        +

        <TYPE, SELF> filter(arr, fn, opt_self)code »

        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.

        +
        Parameters
        arr(Array<TYPE>|webdriver.promise.Promise<Array<TYPE>>)

        The +array to iterator over, or a promise that will resolve to said array.

        +
        fnfunction(this: SELF, TYPE, number, Array<TYPE>): ?(boolean|webdriver.promise.Promise<boolean>)

        The function +to call for each element in the array.

        +
        opt_self?SELF=

        The object to be used as the value of 'this' within +fn.

        +

        <T> fulfilled(opt_value)code »

        Creates a promise that has been resolved with the given value.

        +
        Parameters
        opt_value?T=

        The resolved value.

        +
        Returns
        webdriver.promise.Promise<T>

        The resolved promise.

        +

        fullyResolved(value)code »

        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 value and not a copy.

        +

        Warning: This function makes no checks against objects that contain +cyclical references:

        +
        var value = {};
        +value['self'] = value;
        +promise.fullyResolved(value);  // Stack overflow.
        +
        +
        Parameters
        value*

        The value to fully resolve.

        +
        Returns
        webdriver.promise.Promise

        A promise for a fully resolved version +of the input value.

        +

        isGenerator(fn)code »

        Tests is a function is a generator.

        +
        Parameters
        fnFunction

        The function to test.

        +
        Returns
        boolean

        Whether the function is a generator.

        +

        isPromise(value)code »

        Determines whether a value should be treated as a promise. +Any object whose "then" property is a function will be considered a promise.

        +
        Parameters
        value*

        The value to test.

        +
        Returns
        boolean

        Whether the value is a promise.

        +

        <TYPE, SELF> map(arr, fn, opt_self)code »

        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.

        +
        Parameters
        arr(Array<TYPE>|webdriver.promise.Promise<Array<TYPE>>)

        The +array to iterator over, or a promise that will resolve to said array.

        +
        fnfunction(this: SELF, TYPE, number, Array<TYPE>): ?

        The +function to call for each element in the array. This function should +expect three arguments (the element, the index, and the array itself.

        +
        opt_self?SELF=

        The object to be used as the value of 'this' within +fn.

        +

        <T> rejected(opt_reason)code »

        Creates a promise that has been rejected with the given reason.

        +
        Parameters
        opt_reason*=

        The rejection reason; may be any value, but is +usually an Error or a string.

        +
        Returns
        webdriver.promise.Promise<T>

        The rejected promise.

        +

        setDefaultFlow(flow)code »

        Changes the default flow to use when no others are active.

        +
        Parameters
        flowwebdriver.promise.ControlFlow

        The new default flow.

        +
        Throws
        Error

        If the default flow is not currently active.

        +

        when(value, opt_callback, opt_errback)code »

        Registers an observer on a promised value, 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.

        +
        Parameters
        value*

        The value to observe.

        +
        opt_callback?Function=

        The function to call when the value is +resolved successfully.

        +
        opt_errback?Function=

        The function to call when the value is +rejected.

        +
        Returns
        webdriver.promise.Promise

        A new promise.

        +

        Compiler Constants

        LONG_STACK_TRACESboolean

        Whether to append traces of then to rejection +errors.

        +

        Types

        CancellationError

        Error used when the computation of a promise is cancelled.

        +
        ControlFlow

        Handles the execution of scheduled tasks, each of which may be an +asynchronous operation.

        +
        Deferred

        Represents a value that will be resolved at some point in the future.

        +
        MultipleUnhandledRejectionError

        Error used when there are multiple unhandled promise rejections detected +within a task or callback.

        +
        Promise

        Represents the eventual value of a completed operation.

        +
        Thenable

        No description.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_stacktrace.html b/docs/namespace_webdriver_stacktrace.html index 3eb1898..bd526fd 100644 --- a/docs/namespace_webdriver_stacktrace.html +++ b/docs/namespace_webdriver_stacktrace.html @@ -1,39 +1,17 @@ -webdriver.stacktrace

        Namespace webdriver.stacktrace

        code »

        Classes

        webdriver.stacktrace.Frame
        Class representing one stack frame.
        webdriver.stacktrace.Snapshot
        Stores a snapshot of the stack trace at the time this instance was created.
        Show:

        Global Functions

        code »webdriver.stacktrace.format ( error )!(Error|goog.testing.JsUnitException)

        Formats an error's stack trace.

        Parameters
        error: !(Error|goog.testing.JsUnitException)
        The error to format.
        Returns
        The formatted error.

        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.

        Returns
        The frames of the stack trace.

        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.

        Parameters
        error: !(Error|goog.testing.JsUnitException)
        The error.
        Returns
        The stack trace string.

        Parses a long firefox stack frame.

        Parameters
        frameStr: string
        The stack frame as string.
        Returns
        Stack frame object.

        Parses one stack frame.

        Parameters
        frameStr: string
        The stack frame as string.
        Returns
        Stack frame object or null if the - parsing failed.

        Parses an Error object's stack trace.

        Parameters
        stack: string
        The stack trace.
        Returns
        Stack frames. The - unrecognized frames will be nulled out.

        Global Properties

        Representation of an anonymous frame in a stack trace generated by - goog.testing.stacktrace.

        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 - goog.testing.stacktrace.

        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 - goog.testing.stacktrace.

        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.

        \ No newline at end of file +webdriver.stacktrace

        namespace webdriver.stacktrace

        Functions

        format(error)code »

        Formats an error's stack trace.

        +
        Parameters
        errorError

        The error to format.

        +
        Returns
        Error

        The formatted error.

        +

        get()code »

        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.

        +
        Returns
        Array<webdriver.stacktrace.Frame>

        The frames of the stack trace.

        +

        getStack(error)code »

        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.

        +
        Parameters
        errorError

        The error.

        +
        Returns
        string

        The stack trace string.

        +

        Properties

        BROWSER_SUPPORTEDboolean

        Whether the current browser supports stack traces.

        +

        Types

        Frame

        Class representing one stack frame.

        +
        Snapshot

        Stores a snapshot of the stack trace at the time this instance was created.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_testing.html b/docs/namespace_webdriver_testing.html index e077f7b..07bbe37 100644 --- a/docs/namespace_webdriver_testing.html +++ b/docs/namespace_webdriver_testing.html @@ -1 +1,7 @@ -webdriver.testing

        Namespace webdriver.testing

        code »

        Classes

        webdriver.testing.Assertion
        Utility for performing assertions against a given value.
        webdriver.testing.ContainsMatcher
        Accepts strins or array-like structures that contain value.
        webdriver.testing.NegatedAssertion
        An assertion that negates any applied matchers.
        Show:

        Global Functions

        Creates a new assertion.

        Parameters
        value: *
        The value to perform an assertion on.
        Returns
        The new assertion.
        \ No newline at end of file +webdriver.testing
        \ No newline at end of file diff --git a/docs/namespace_webdriver_testing_assert.html b/docs/namespace_webdriver_testing_assert.html index 91e04b6..cc00e42 100644 --- a/docs/namespace_webdriver_testing_assert.html +++ b/docs/namespace_webdriver_testing_assert.html @@ -1,3 +1,9 @@ -webdriver.testing.assert

        Namespace webdriver.testing.assert

        code »

        Creates a new assertion.

        Main

        assert ( value )!webdriver.testing.Assertion
        Parameters
        value: *
        The value to perform an assertion on.
        Returns
        The new assertion.
        Show:

        Global Functions

        Registers a new assertion to expose from the - webdriver.testing.Assertion prototype.

        Parameters
        name: string
        The assertion name.
        matcherTemplate: (function(new: goog.labs.testing.Matcher, *)|{matches: function(*): boolean, describe: function(): string})
        Either the - matcher constructor to use, or an object literal defining a matcher.
        \ No newline at end of file +webdriver.testing.assert

        namespace webdriver.testing.assert

        Creates a new assertion.

        +

        webdriver.testing.assert(value)

        Parameters
        value*

        The value to perform an assertion on.

        +
        Returns
        webdriver.testing.Assertion

        The new assertion.

        +

        Functions

        register(name, matcherTemplate)code »

        Registers a new assertion to expose from the +webdriver.testing.Assertion prototype.

        +
        Parameters
        namestring

        The assertion name.

        +
        matcherTemplate(function(new: goog.labs.testing.Matcher, *): ?|{describe: function(): string, matches: function(*): boolean})

        Either the +matcher constructor to use, or an object literal defining a matcher.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_testing_asserts.html b/docs/namespace_webdriver_testing_asserts.html index 1050173..7b59d1b 100644 --- a/docs/namespace_webdriver_testing_asserts.html +++ b/docs/namespace_webdriver_testing_asserts.html @@ -1,10 +1,18 @@ -webdriver.testing.asserts

        Namespace webdriver.testing.asserts

        code »
        Show:

        Global Functions

        code »webdriver.testing.asserts.assertThat ( failureMessageOrActualValue, actualValueOrMatcher, opt_matcher )!webdriver.promise.Promise
        Deprecated: Use webdriver.testing.asserts.assert instead.

        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)

        Parameters
        failureMessageOrActualValue: *
        Either a failure message or the value - to apply to the given matcher.
        actualValueOrMatcher: *
        Either the value to apply to the given - matcher, or the matcher itself.
        opt_matcher: goog.labs.testing.Matcher=
        The matcher to use; - ignored unless this function is invoked with three arguments.
        Returns
        The assertion result.

        Creates an equality matcher.

        Parameters
        expected: *
        The expected value.
        Returns
        The new matcher.
        \ No newline at end of file +webdriver.testing.asserts

        namespace webdriver.testing.asserts

        Functions

        assertThat(failureMessageOrActualValue, actualValueOrMatcher, opt_matcher)code »

        deprecated

        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)

        +
        Deprecated

        Use webdriver.testing.asserts.assert instead.

        +
        Parameters
        failureMessageOrActualValue*

        Either a failure message or the value +to apply to the given matcher.

        +
        actualValueOrMatcher*

        Either the value to apply to the given +matcher, or the matcher itself.

        +
        opt_matcher?goog.labs.testing.Matcher=

        The matcher to use; +ignored unless this function is invoked with three arguments.

        +
        Returns
        webdriver.promise.Promise

        The assertion result.

        +

        equalTo(expected)code »

        Creates an equality matcher.

        +
        Parameters
        expected*

        The expected value.

        +
        Returns
        goog.labs.testing.Matcher

        The new matcher.

        +
        \ No newline at end of file diff --git a/docs/namespace_webdriver_until.html b/docs/namespace_webdriver_until.html new file mode 100644 index 0000000..fdc7557 --- /dev/null +++ b/docs/namespace_webdriver_until.html @@ -0,0 +1,82 @@ +webdriver.until

        namespace webdriver.until

        Functions

        ableToSwitchToFrame(frame)code »

        Creates a condition that will wait until the input driver is able to switch +to the designated frame. The target frame may be specified as

        +
        1. a numeric index into +window.frames +for the currently selected frame.
        2. a webdriver.WebElement, which must reference a FRAME or IFRAME +element on the current page.
        3. a locator which may be used to first locate a FRAME or IFRAME on the +current page before attempting to switch to it.
        +

        Upon successful resolution of this condition, the driver will be left +focused on the new frame.

        +
        Parameters
        frame(number|webdriver.WebElement|webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The frame identifier.

        +
        Returns
        webdriver.until.Condition<boolean>

        A new condition.

        +

        alertIsPresent()code »

        Creates a condition that waits for an alert to be opened. Upon success, the +returned promise will be fulfilled with the handle for the opened alert.

        +
        Returns
        webdriver.until.Condition<webdriver.Alert>

        The new condition.

        +

        elementIsDisabled(element)code »

        Creates a condition that will wait for the given element to be disabled.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsEnabled(element)code »

        Creates a condition that will wait for the given element to be enabled.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsNotSelected(element)code »

        Creates a condition that will wait for the given element to be deselected.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsNotVisible(element)code »

        Creates a condition that will wait for the given element to be in the DOM, +yet not visible to the user.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsSelected(element)code »

        Creates a condition that will wait for the given element to be selected.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementIsVisible(element)code »

        Creates a condition that will wait for the given element to become visible.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementLocated(locator)code »

        Creates a condition that will loop until an element is +found with the given locator.

        +
        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +to use.

        +

        elementTextContains(element, substr)code »

        Creates a condition that will wait for the given element's +visible text to contain the given +substring.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        substrstring

        The substring to search for.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementTextIs(element, text)code »

        Creates a condition that will wait for the given element's +visible text to match the given +text exactly.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        textstring

        The expected text.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementTextMatches(element, regex)code »

        Creates a condition that will wait for the given element's +visible text to match a regular +expression.

        +
        Parameters
        elementwebdriver.WebElement

        The element to test.

        +
        regexRegExp

        The regular expression to test against.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        elementsLocated(locator)code »

        Creates a condition that will loop until at least one element is +found with the given locator.

        +
        Parameters
        locator(webdriver.Locator|{className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})

        The locator +to use.

        +

        stalenessOf(element)code »

        Creates a condition that will wait for the given element to become stale. An +element is considered stale once it is removed from the DOM, or a new page +has loaded.

        +
        Parameters
        elementwebdriver.WebElement

        The element that should become stale.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        titleContains(substr)code »

        Creates a condition that will wait for the current page's title to contain +the given substring.

        +
        Parameters
        substrstring

        The substring that should be present in the page +title.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        titleIs(title)code »

        Creates a condition that will wait for the current page's title to match the +given value.

        +
        Parameters
        titlestring

        The expected page title.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        titleMatches(regex)code »

        Creates a condition that will wait for the current page's title to match the +given regular expression.

        +
        Parameters
        regexRegExp

        The regular expression to test against.

        +
        Returns
        webdriver.until.Condition<boolean>

        The new condition.

        +

        Types

        Condition

        Defines a condition to

        +
        \ No newline at end of file diff --git a/docs/source/_base.js.src.html b/docs/source/_base.js.src.html index a8ca370..7fa1c82 100644 --- a/docs/source/_base.js.src.html +++ b/docs/source/_base.js.src.html @@ -1 +1 @@ -_base.js

        _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
        31var 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 */
        41var 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. */
        48function isDevMode() {
        49 return devMode;
        50}
        51
        52
        53/**
        54 * @type {string} Path to Closure's base file, relative to this module.
        55 * @const
        56 */
        57var 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 */
        69var 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 */
        82function 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 */
        94var 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});
        112closure.window = closure;
        113
        114
        115loadScript(CLOSURE_BASE_FILE_PATH);
        116loadScript(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 */
        125function 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 */
        144exports.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
        161if (isDevMode()) {
        162 exports.closure = closure;
        163}
        164exports.isDevMode = isDevMode;
        165exports.require = closureRequire;
        \ No newline at end of file +_base.js

        _base.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview The base module responsible for bootstrapping the Closure
        20 * library and providing a means of loading Closure-based modules.
        21 *
        22 * <p>Each script loaded by this module will be granted access to this module's
        23 * {@code require} function; all required non-native modules must be specified
        24 * relative to <em>this</em> module.
        25 *
        26 * <p>This module will load all scripts from the "lib" subdirectory, unless the
        27 * SELENIUM_DEV_MODE environment variable has been set to 1, in which case all
        28 * scripts will be loaded from the Selenium client containing this script.
        29 */
        30
        31'use strict';
        32
        33var fs = require('fs'),
        34 path = require('path'),
        35 vm = require('vm');
        36
        37
        38/**
        39 * If this script was loaded from the Selenium project repo, it will operate in
        40 * development mode, adjusting how it loads Closure-based dependencies.
        41 * @type {boolean}
        42 */
        43var devMode = (function() {
        44 var buildDescFile = path.join(__dirname, '..', 'build.desc');
        45 return fs.existsSync(buildDescFile);
        46})();
        47
        48
        49/** @return {boolean} Whether this script was loaded in dev mode. */
        50function isDevMode() {
        51 return devMode;
        52}
        53
        54
        55/**
        56 * @type {string} Path to Closure's base file, relative to this module.
        57 * @const
        58 */
        59var CLOSURE_BASE_FILE_PATH = (function() {
        60 var relativePath = isDevMode() ?
        61 '../../../third_party/closure/goog/base.js' :
        62 './lib/goog/base.js';
        63 return path.join(__dirname, relativePath);
        64})();
        65
        66
        67/**
        68 * @type {string} Path to Closure's base file, relative to this module.
        69 * @const
        70 */
        71var DEPS_FILE_PATH = (function() {
        72 var relativePath = isDevMode() ?
        73 '../../../javascript/deps.js' :
        74 './lib/goog/deps.js';
        75 return path.join(__dirname, relativePath);
        76})();
        77
        78
        79/**
        80 * Maintains a unique context for Closure library-based code.
        81 * @param {boolean=} opt_configureForTesting Whether to configure a fake DOM
        82 * for Closure-testing code that (incorrectly) assumes a DOM is always
        83 * present.
        84 * @constructor
        85 */
        86function Context(opt_configureForTesting) {
        87 var closure = this.closure = vm.createContext({
        88 console: console,
        89 setTimeout: setTimeout,
        90 setInterval: setInterval,
        91 clearTimeout: clearTimeout,
        92 clearInterval: clearInterval,
        93 process: process,
        94 require: require,
        95 Buffer: Buffer,
        96 Error: Error,
        97 TypeError: TypeError,
        98 CLOSURE_BASE_PATH: path.dirname(CLOSURE_BASE_FILE_PATH) + '/',
        99 CLOSURE_IMPORT_SCRIPT: function(src, opt_srcText) {
        100 if (opt_srcText !== undefined) {
        101 // Windows paths use backslashes, which must be properly escaped before
        102 // evaluated with vm.runInContext.
        103 opt_srcText = opt_srcText.replace(/\\/g, '/');
        104 vm.runInContext(opt_srcText, closure, src);
        105 } else {
        106 loadScript(src);
        107 }
        108 return true;
        109 },
        110 CLOSURE_NO_DEPS: !isDevMode(),
        111 CLOSURE_UNCOMPILED_DEFINES: {'goog.json.USE_NATIVE_JSON': true},
        112 goog: {}
        113 });
        114 closure.window = closure.top = closure;
        115
        116 if (opt_configureForTesting) {
        117 closure.document = {
        118 body: {},
        119 createElement: function() { return {}; },
        120 getElementsByTagName: function() { return []; }
        121 };
        122 closure.document.body.ownerDocument = closure.document;
        123 }
        124
        125 loadScript(CLOSURE_BASE_FILE_PATH);
        126 loadScript(DEPS_FILE_PATH);
        127
        128 // Redefine retrieveAndExecModule_ to load modules. Closure's version
        129 // assumes XMLHttpRequest is defined (and by extension that scripts
        130 // are being loaded from a server).
        131 closure.goog.retrieveAndExecModule_ = function(src) {
        132 var normalizedSrc = path.normalize(src);
        133 var contents = fs.readFileSync(normalizedSrc, 'utf8');
        134 contents = closure.goog.wrapModule_(src, contents);
        135 vm.runInContext(contents, closure, normalizedSrc);
        136 };
        137
        138 /**
        139 * Synchronously loads a script into the protected Closure context.
        140 * @param {string} src Path to the file to load.
        141 */
        142 function loadScript(src) {
        143 src = path.normalize(src);
        144 var contents = fs.readFileSync(src, 'utf8');
        145 vm.runInContext(contents, closure, src);
        146 }
        147}
        148
        149
        150var context = new Context();
        151
        152
        153/**
        154 * Loads a symbol by name from the protected Closure context.
        155 * @param {string} symbol The symbol to load.
        156 * @return {?} The loaded symbol, or {@code null} if not found.
        157 * @throws {Error} If the symbol has not been defined.
        158 */
        159function closureRequire(symbol) {
        160 context.closure.goog.require(symbol);
        161 return context.closure.goog.getObjectByName(symbol);
        162}
        163
        164
        165// PUBLIC API
        166
        167
        168/**
        169 * Loads a symbol by name from the protected Closure context and exports its
        170 * public API to the provided object. This function relies on Closure code
        171 * conventions to define the public API of an object as those properties whose
        172 * name does not end with "_".
        173 * @param {string} symbol The symbol to load. This must resolve to an object.
        174 * @return {!Object} An object with the exported API.
        175 * @throws {Error} If the symbol has not been defined or does not resolve to
        176 * an object.
        177 */
        178exports.exportPublicApi = function(symbol) {
        179 var src = closureRequire(symbol);
        180 if (typeof src != 'object' || src === null) {
        181 throw Error('"' + symbol + '" must resolve to an object');
        182 }
        183
        184 var dest = {};
        185 Object.keys(src).forEach(function(key) {
        186 if (key[key.length - 1] != '_') {
        187 dest[key] = src[key];
        188 }
        189 });
        190
        191 return dest;
        192};
        193
        194
        195if (isDevMode()) {
        196 exports.closure = context.closure;
        197}
        198exports.Context = Context;
        199exports.isDevMode = isDevMode;
        200exports.require = closureRequire;
        \ No newline at end of file diff --git a/docs/source/builder.js.src.html b/docs/source/builder.js.src.html index 4ab2772..40be1bc 100644 --- a/docs/source/builder.js.src.html +++ b/docs/source/builder.js.src.html @@ -1 +1 @@ -builder.js

        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
        15var base = require('./_base'),
        16 executors = require('./executors');
        17
        18var 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 */
        31function 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 */
        57var Builder = function() {
        58 goog.base(this);
        59};
        60goog.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 */
        70Builder.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 */
        81Builder.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 */
        90Builder.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
        113exports.Builder = Builder;
        \ No newline at end of file +builder.js

        builder.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18var base = require('./_base'),
        19 executors = require('./executors');
        20
        21// Use base.require to avoid circular references between index and this module.
        22var Browser = base.require('webdriver.Browser'),
        23 Capabilities = base.require('webdriver.Capabilities'),
        24 Capability = base.require('webdriver.Capability'),
        25 WebDriver = base.require('webdriver.WebDriver'),
        26 promise = base.require('webdriver.promise');
        27
        28
        29
        30var seleniumServer;
        31
        32/**
        33 * Starts an instance of the Selenium server if not yet running.
        34 * @param {string} jar Path to the server jar to use.
        35 * @return {!webdriver.promise.Promise<string>} A promise for the server's
        36 * addrss once started.
        37 */
        38function startSeleniumServer(jar) {
        39 if (!seleniumServer) {
        40 // Requiring 'chrome' above would create a cycle:
        41 // index -> builder -> chrome -> index
        42 var remote = require('./remote');
        43 seleniumServer = new remote.SeleniumServer(jar);
        44 }
        45 return seleniumServer.start();
        46}
        47
        48
        49/**
        50 * Creates new {@link webdriver.WebDriver WebDriver} instances. The environment
        51 * variables listed below may be used to override a builder's configuration,
        52 * allowing quick runtime changes.
        53 *
        54 * - {@code SELENIUM_BROWSER}: defines the target browser in the form
        55 * {@code browser[:version][:platform]}.
        56 *
        57 * - {@code SELENIUM_REMOTE_URL}: defines the remote URL for all builder
        58 * instances. This environment variable should be set to a fully qualified
        59 * URL for a WebDriver server (e.g. http://localhost:4444/wd/hub). This
        60 * option always takes precedence over {@code SELENIUM_SERVER_JAR}.
        61 *
        62 * - {@code SELENIUM_SERVER_JAR}: defines the path to the
        63 * <a href="http://selenium-release.storage.googleapis.com/index.html">
        64 * standalone Selenium server</a> jar to use. The server will be started the
        65 * first time a WebDriver instance and be killed when the process exits.
        66 *
        67 * Suppose you had mytest.js that created WebDriver with
        68 *
        69 * var driver = new webdriver.Builder()
        70 * .forBrowser('chrome')
        71 * .build();
        72 *
        73 * This test could be made to use Firefox on the local machine by running with
        74 * `SELENIUM_BROWSER=firefox node mytest.js`. Rather than change the code to
        75 * target Google Chrome on a remote machine, you can simply set the
        76 * `SELENIUM_BROWSER` and `SELENIUM_REMOTE_URL` environment variables:
        77 *
        78 * SELENIUM_BROWSER=chrome:36:LINUX \
        79 * SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \
        80 * node mytest.js
        81 *
        82 * You could also use a local copy of the standalone Selenium server:
        83 *
        84 * SELENIUM_BROWSER=chrome:36:LINUX \
        85 * SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \
        86 * node mytest.js
        87 *
        88 * @constructor
        89 */
        90var Builder = function() {
        91
        92 /** @private {webdriver.promise.ControlFlow} */
        93 this.flow_ = null;
        94
        95 /** @private {string} */
        96 this.url_ = '';
        97
        98 /** @private {?string} */
        99 this.proxy_ = null;
        100
        101 /** @private {!webdriver.Capabilities} */
        102 this.capabilities_ = new Capabilities();
        103
        104 /** @private {chrome.Options} */
        105 this.chromeOptions_ = null;
        106
        107 /** @private {firefox.Options} */
        108 this.firefoxOptions_ = null;
        109
        110 /** @private {opera.Options} */
        111 this.operaOptions_ = null;
        112
        113 /** @private {ie.Options} */
        114 this.ieOptions_ = null;
        115
        116 /** @private {safari.Options} */
        117 this.safariOptions_ = null;
        118
        119 /** @private {boolean} */
        120 this.ignoreEnv_ = false;
        121};
        122
        123
        124/**
        125 * Configures this builder to ignore any environment variable overrides and to
        126 * only use the configuration specified through this instance's API.
        127 *
        128 * @return {!Builder} A self reference.
        129 */
        130Builder.prototype.disableEnvironmentOverrides = function() {
        131 this.ignoreEnv_ = true;
        132 return this;
        133};
        134
        135
        136/**
        137 * Sets the URL of a remote WebDriver server to use. Once a remote URL has been
        138 * specified, the builder direct all new clients to that server. If this method
        139 * is never called, the Builder will attempt to create all clients locally.
        140 *
        141 * As an alternative to this method, you may also set the `SELENIUM_REMOTE_URL`
        142 * environment variable.
        143 *
        144 * @param {string} url The URL of a remote server to use.
        145 * @return {!Builder} A self reference.
        146 */
        147Builder.prototype.usingServer = function(url) {
        148 this.url_ = url;
        149 return this;
        150};
        151
        152
        153/**
        154 * @return {string} The URL of the WebDriver server this instance is configured
        155 * to use.
        156 */
        157Builder.prototype.getServerUrl = function() {
        158 return this.url_;
        159};
        160
        161
        162/**
        163 * Sets the URL of the proxy to use for the WebDriver's HTTP connections.
        164 * If this method is never called, the Builder will create a connection without
        165 * a proxy.
        166 *
        167 * @param {string} proxy The URL of a proxy to use.
        168 * @return {!Builder} A self reference.
        169 */
        170Builder.prototype.usingWebDriverProxy = function(proxy) {
        171 this.proxy_ = proxy;
        172 return this;
        173};
        174
        175
        176/**
        177 * @return {string} The URL of the proxy server to use for the WebDriver's HTTP
        178 * connections.
        179 */
        180Builder.prototype.getWebDriverProxy = function() {
        181 return this.proxy_;
        182};
        183
        184
        185/**
        186 * Sets the desired capabilities when requesting a new session. This will
        187 * overwrite any previously set capabilities.
        188 * @param {!(Object|webdriver.Capabilities)} capabilities The desired
        189 * capabilities for a new session.
        190 * @return {!Builder} A self reference.
        191 */
        192Builder.prototype.withCapabilities = function(capabilities) {
        193 this.capabilities_ = new Capabilities(capabilities);
        194 return this;
        195};
        196
        197
        198/**
        199 * Returns the base set of capabilities this instance is currently configured
        200 * to use.
        201 * @return {!webdriver.Capabilities} The current capabilities for this builder.
        202 */
        203Builder.prototype.getCapabilities = function() {
        204 return this.capabilities_;
        205};
        206
        207
        208/**
        209 * Configures the target browser for clients created by this instance.
        210 * Any calls to {@link #withCapabilities} after this function will
        211 * overwrite these settings.
        212 *
        213 * You may also define the target browser using the {@code SELENIUM_BROWSER}
        214 * environment variable. If set, this environment variable should be of the
        215 * form `browser[:[version][:platform]]`.
        216 *
        217 * @param {(string|webdriver.Browser)} name The name of the target browser;
        218 * common defaults are available on the {@link webdriver.Browser} enum.
        219 * @param {string=} opt_version A desired version; may be omitted if any
        220 * version should be used.
        221 * @param {string=} opt_platform The desired platform; may be omitted if any
        222 * version may be used.
        223 * @return {!Builder} A self reference.
        224 */
        225Builder.prototype.forBrowser = function(name, opt_version, opt_platform) {
        226 this.capabilities_.set(Capability.BROWSER_NAME, name);
        227 this.capabilities_.set(Capability.VERSION, opt_version || null);
        228 this.capabilities_.set(Capability.PLATFORM, opt_platform || null);
        229 return this;
        230};
        231
        232
        233/**
        234 * Sets the proxy configuration to use for WebDriver clients created by this
        235 * builder. Any calls to {@link #withCapabilities} after this function will
        236 * overwrite these settings.
        237 * @param {!webdriver.ProxyConfig} config The configuration to use.
        238 * @return {!Builder} A self reference.
        239 */
        240Builder.prototype.setProxy = function(config) {
        241 this.capabilities_.setProxy(config);
        242 return this;
        243};
        244
        245
        246/**
        247 * Sets the logging preferences for the created session. Preferences may be
        248 * changed by repeated calls, or by calling {@link #withCapabilities}.
        249 * @param {!(webdriver.logging.Preferences|Object.<string, string>)} prefs The
        250 * desired logging preferences.
        251 * @return {!Builder} A self reference.
        252 */
        253Builder.prototype.setLoggingPrefs = function(prefs) {
        254 this.capabilities_.setLoggingPrefs(prefs);
        255 return this;
        256};
        257
        258
        259/**
        260 * Sets whether native events should be used.
        261 * @param {boolean} enabled Whether to enable native events.
        262 * @return {!Builder} A self reference.
        263 */
        264Builder.prototype.setEnableNativeEvents = function(enabled) {
        265 this.capabilities_.setEnableNativeEvents(enabled);
        266 return this;
        267};
        268
        269
        270/**
        271 * Sets how elements should be scrolled into view for interaction.
        272 * @param {number} behavior The desired scroll behavior: either 0 to align with
        273 * the top of the viewport or 1 to align with the bottom.
        274 * @return {!Builder} A self reference.
        275 */
        276Builder.prototype.setScrollBehavior = function(behavior) {
        277 this.capabilities_.setScrollBehavior(behavior);
        278 return this;
        279};
        280
        281
        282/**
        283 * Sets the default action to take with an unexpected alert before returning
        284 * an error.
        285 * @param {string} beahvior The desired behavior; should be "accept", "dismiss",
        286 * or "ignore". Defaults to "dismiss".
        287 * @return {!Builder} A self reference.
        288 */
        289Builder.prototype.setAlertBehavior = function(behavior) {
        290 this.capabilities_.setAlertBehavior(behavior);
        291 return this;
        292};
        293
        294
        295/**
        296 * Sets Chrome specific {@linkplain selenium-webdriver/chrome.Options options}
        297 * for drivers created by this builder. Any logging or proxy settings defined
        298 * on the given options will take precedence over those set through
        299 * {@link #setLoggingPrefs} and {@link #setProxy}, respectively.
        300 *
        301 * @param {!chrome.Options} options The ChromeDriver options to use.
        302 * @return {!Builder} A self reference.
        303 */
        304Builder.prototype.setChromeOptions = function(options) {
        305 this.chromeOptions_ = options;
        306 return this;
        307};
        308
        309
        310/**
        311 * Sets Firefox specific {@linkplain selenium-webdriver/firefox.Options options}
        312 * for drivers created by this builder. Any logging or proxy settings defined
        313 * on the given options will take precedence over those set through
        314 * {@link #setLoggingPrefs} and {@link #setProxy}, respectively.
        315 *
        316 * @param {!firefox.Options} options The FirefoxDriver options to use.
        317 * @return {!Builder} A self reference.
        318 */
        319Builder.prototype.setFirefoxOptions = function(options) {
        320 this.firefoxOptions_ = options;
        321 return this;
        322};
        323
        324
        325/**
        326 * Sets Opera specific {@linkplain selenium-webdriver/opera.Options options} for
        327 * drivers created by this builder. Any logging or proxy settings defined on the
        328 * given options will take precedence over those set through
        329 * {@link #setLoggingPrefs} and {@link #setProxy}, respectively.
        330 *
        331 * @param {!opera.Options} options The OperaDriver options to use.
        332 * @return {!Builder} A self reference.
        333 */
        334Builder.prototype.setOperaOptions = function(options) {
        335 this.operaOptions_ = options;
        336 return this;
        337};
        338
        339
        340/**
        341 * Sets Internet Explorer specific
        342 * {@linkplain selenium-webdriver/ie.Options options} for drivers created by
        343 * this builder. Any proxy settings defined on the given options will take
        344 * precedence over those set through {@link #setProxy}.
        345 *
        346 * @param {!ie.Options} options The IEDriver options to use.
        347 * @return {!Builder} A self reference.
        348 */
        349Builder.prototype.setIeOptions = function(options) {
        350 this.ieOptions_ = options;
        351 return this;
        352};
        353
        354
        355/**
        356 * Sets Safari specific {@linkplain selenium-webdriver/safari.Options options}
        357 * for drivers created by this builder. Any logging settings defined on the
        358 * given options will take precedence over those set through
        359 * {@link #setLoggingPrefs}.
        360 *
        361 * @param {!safari.Options} options The Safari options to use.
        362 * @return {!Builder} A self reference.
        363 */
        364Builder.prototype.setSafariOptions = function(options) {
        365 this.safariOptions_ = options;
        366 return this;
        367};
        368
        369
        370/**
        371 * Sets the control flow that created drivers should execute actions in. If
        372 * the flow is never set, or is set to {@code null}, it will use the active
        373 * flow at the time {@link #build()} is called.
        374 * @param {webdriver.promise.ControlFlow} flow The control flow to use, or
        375 * {@code null} to
        376 * @return {!Builder} A self reference.
        377 */
        378Builder.prototype.setControlFlow = function(flow) {
        379 this.flow_ = flow;
        380 return this;
        381};
        382
        383
        384/**
        385 * Creates a new WebDriver client based on this builder's current
        386 * configuration.
        387 *
        388 * @return {!webdriver.WebDriver} A new WebDriver instance.
        389 * @throws {Error} If the current configuration is invalid.
        390 */
        391Builder.prototype.build = function() {
        392 // Create a copy for any changes we may need to make based on the current
        393 // environment.
        394 var capabilities = new Capabilities(this.capabilities_);
        395
        396 var browser;
        397 if (!this.ignoreEnv_ && process.env.SELENIUM_BROWSER) {
        398 browser = process.env.SELENIUM_BROWSER.split(/:/, 3);
        399 capabilities.set(Capability.BROWSER_NAME, browser[0]);
        400 capabilities.set(Capability.VERSION, browser[1] || null);
        401 capabilities.set(Capability.PLATFORM, browser[2] || null);
        402 }
        403
        404 browser = capabilities.get(Capability.BROWSER_NAME);
        405
        406 if (typeof browser !== 'string') {
        407 throw TypeError(
        408 'Target browser must be a string, but is <' + (typeof browser) + '>;' +
        409 ' did you forget to call forBrowser()?');
        410 }
        411
        412 if (browser === 'ie') {
        413 browser = Browser.INTERNET_EXPLORER;
        414 }
        415
        416 // Apply browser specific overrides.
        417 if (browser === Browser.CHROME && this.chromeOptions_) {
        418 capabilities.merge(this.chromeOptions_.toCapabilities());
        419
        420 } else if (browser === Browser.FIREFOX && this.firefoxOptions_) {
        421 capabilities.merge(this.firefoxOptions_.toCapabilities());
        422
        423 } else if (browser === Browser.INTERNET_EXPLORER && this.ieOptions_) {
        424 capabilities.merge(this.ieOptions_.toCapabilities());
        425
        426 } else if (browser === Browser.OPERA && this.operaOptions_) {
        427 capabilities.merge(this.operaOptions_.toCapabilities());
        428
        429 } else if (browser === Browser.SAFARI && this.safariOptions_) {
        430 capabilities.merge(this.safariOptions_.toCapabilities());
        431 }
        432
        433 // Check for a remote browser.
        434 var url = this.url_;
        435 if (!this.ignoreEnv_) {
        436 if (process.env.SELENIUM_REMOTE_URL) {
        437 url = process.env.SELENIUM_REMOTE_URL;
        438 } else if (process.env.SELENIUM_SERVER_JAR) {
        439 url = startSeleniumServer(process.env.SELENIUM_SERVER_JAR);
        440 }
        441 }
        442
        443 if (url) {
        444 var executor = executors.createExecutor(url, this.proxy_);
        445 return WebDriver.createSession(executor, capabilities, this.flow_);
        446 }
        447
        448 // Check for a native browser.
        449 switch (browser) {
        450 case Browser.CHROME:
        451 // Requiring 'chrome' above would create a cycle:
        452 // index -> builder -> chrome -> index
        453 var chrome = require('./chrome');
        454 return new chrome.Driver(capabilities, null, this.flow_);
        455
        456 case Browser.FIREFOX:
        457 // Requiring 'firefox' above would create a cycle:
        458 // index -> builder -> firefox -> index
        459 var firefox = require('./firefox');
        460 return new firefox.Driver(capabilities, this.flow_);
        461
        462 case Browser.INTERNET_EXPLORER:
        463 // Requiring 'ie' above would create a cycle:
        464 // index -> builder -> ie -> index
        465 var ie = require('./ie');
        466 return new ie.Driver(capabilities, this.flow_);
        467
        468 case Browser.OPERA:
        469 // Requiring 'opera' would create a cycle:
        470 // index -> builder -> opera -> index
        471 var opera = require('./opera');
        472 return new opera.Driver(capabilities, this.flow_);
        473
        474 case Browser.PHANTOM_JS:
        475 // Requiring 'phantomjs' would create a cycle:
        476 // index -> builder -> phantomjs -> index
        477 var phantomjs = require('./phantomjs');
        478 return new phantomjs.Driver(capabilities, this.flow_);
        479
        480 case Browser.SAFARI:
        481 // Requiring 'safari' would create a cycle:
        482 // index -> builder -> safari -> index
        483 var safari = require('./safari');
        484 return new safari.Driver(capabilities, this.flow_);
        485
        486 default:
        487 throw new Error('Do not know how to build driver: ' + browser
        488 + '; did you forget to call usingServer(url)?');
        489 }
        490};
        491
        492
        493// PUBLIC API
        494
        495
        496exports.Builder = Builder;
        \ No newline at end of file diff --git a/docs/source/chrome.js.src.html b/docs/source/chrome.js.src.html index 6d4103c..112dd44 100644 --- a/docs/source/chrome.js.src.html +++ b/docs/source/chrome.js.src.html @@ -1 +1 @@ -chrome.js

        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
        18var fs = require('fs'),
        19 util = require('util');
        20
        21var 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 */
        33var 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 */
        47var 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} */
        69ServiceBuilder.prototype.port_ = 0;
        70
        71
        72/** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
        73ServiceBuilder.prototype.stdio_ = 'ignore';
        74
        75
        76/** @private {Object.<string, string>} */
        77ServiceBuilder.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 */
        86ServiceBuilder.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 */
        101ServiceBuilder.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 */
        111ServiceBuilder.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 */
        123ServiceBuilder.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 */
        135ServiceBuilder.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 */
        148ServiceBuilder.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 */
        160ServiceBuilder.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 */
        173ServiceBuilder.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 port: port,
        179 args: webdriver.promise.when(port, function(port) {
        180 return args.concat('--port=' + port);
        181 }),
        182 env: this.env_,
        183 stdio: this.stdio_
        184 });
        185};
        186
        187
        188/** @type {remote.DriverService} */
        189var defaultService = null;
        190
        191
        192/**
        193 * Sets the default service to use for new ChromeDriver instances.
        194 * @param {!remote.DriverService} service The service to use.
        195 * @throws {Error} If the default service is currently running.
        196 */
        197function setDefaultService(service) {
        198 if (defaultService && defaultService.isRunning()) {
        199 throw Error(
        200 'The previously configured ChromeDriver service is still running. ' +
        201 'You must shut it down before you may adjust its configuration.');
        202 }
        203 defaultService = service;
        204}
        205
        206
        207/**
        208 * Returns the default ChromeDriver service. If such a service has not been
        209 * configured, one will be constructed using the default configuration for
        210 * a ChromeDriver executable found on the system PATH.
        211 * @return {!remote.DriverService} The default ChromeDriver service.
        212 */
        213function getDefaultService() {
        214 if (!defaultService) {
        215 defaultService = new ServiceBuilder().build();
        216 }
        217 return defaultService;
        218}
        219
        220
        221/**
        222 * @type {string}
        223 * @const
        224 */
        225var OPTIONS_CAPABILITY_KEY = 'chromeOptions';
        226
        227
        228/**
        229 * Class for managing ChromeDriver specific options.
        230 * @constructor
        231 */
        232var Options = function() {
        233 /** @private {!Array.<string>} */
        234 this.args_ = [];
        235
        236 /** @private {!Array.<(string|!Buffer)>} */
        237 this.extensions_ = [];
        238};
        239
        240
        241/**
        242 * Extracts the ChromeDriver specific options from the given capabilities
        243 * object.
        244 * @param {!webdriver.Capabilities} capabilities The capabilities object.
        245 * @return {!Options} The ChromeDriver options.
        246 */
        247Options.fromCapabilities = function(capabilities) {
        248 var options = new Options();
        249
        250 var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
        251 if (o instanceof Options) {
        252 options = o;
        253 } else if (o) {
        254 options.
        255 addArguments(o.args || []).
        256 addExtensions(o.extensions || []).
        257 detachDriver(!!o.detach).
        258 setChromeBinaryPath(o.binary).
        259 setChromeLogFile(o.logFile).
        260 setLocalState(o.localState).
        261 setUserPreferences(o.prefs);
        262 }
        263
        264 if (capabilities.has(webdriver.Capability.PROXY)) {
        265 options.setProxy(capabilities.get(webdriver.Capability.PROXY));
        266 }
        267
        268 if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) {
        269 options.setLoggingPreferences(
        270 capabilities.get(webdriver.Capability.LOGGING_PREFS));
        271 }
        272
        273 return options;
        274};
        275
        276
        277/**
        278 * Add additional command line arguments to use when launching the Chrome
        279 * browser. Each argument may be specified with or without the "--" prefix
        280 * (e.g. "--foo" and "foo"). Arguments with an associated value should be
        281 * delimited by an "=": "foo=bar".
        282 * @param {...(string|!Array.<string>)} var_args The arguments to add.
        283 * @return {!Options} A self reference.
        284 */
        285Options.prototype.addArguments = function(var_args) {
        286 this.args_ = this.args_.concat.apply(this.args_, arguments);
        287 return this;
        288};
        289
        290
        291/**
        292 * Add additional extensions to install when launching Chrome. Each extension
        293 * should be specified as the path to the packed CRX file, or a Buffer for an
        294 * extension.
        295 * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The
        296 * extensions to add.
        297 * @return {!Options} A self reference.
        298 */
        299Options.prototype.addExtensions = function(var_args) {
        300 this.extensions_ = this.extensions_.concat.apply(
        301 this.extensions_, arguments);
        302 return this;
        303};
        304
        305
        306/**
        307 * Sets the path to the Chrome binary to use. On Mac OS X, this path should
        308 * reference the actual Chrome executable, not just the application binary
        309 * (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
        310 *
        311 * The binary path be absolute or relative to the chromedriver server
        312 * executable, but it must exist on the machine that will launch Chrome.
        313 *
        314 * @param {string} path The path to the Chrome binary to use.
        315 * @return {!Options} A self reference.
        316 */
        317Options.prototype.setChromeBinaryPath = function(path) {
        318 this.binary_ = path;
        319 return this;
        320};
        321
        322
        323/**
        324 * Sets whether to leave the started Chrome browser running if the controlling
        325 * ChromeDriver service is killed before {@link webdriver.WebDriver#quit()} is
        326 * called.
        327 * @param {boolean} detach Whether to leave the browser running if the
        328 * chromedriver service is killed before the session.
        329 * @return {!Options} A self reference.
        330 */
        331Options.prototype.detachDriver = function(detach) {
        332 this.detach_ = detach;
        333 return this;
        334};
        335
        336
        337/**
        338 * Sets the user preferences for Chrome's user profile. See the "Preferences"
        339 * file in Chrome's user data directory for examples.
        340 * @param {!Object} prefs Dictionary of user preferences to use.
        341 * @return {!Options} A self reference.
        342 */
        343Options.prototype.setUserPreferences = function(prefs) {
        344 this.prefs_ = prefs;
        345 return this;
        346};
        347
        348
        349/**
        350 * Sets the logging preferences for the new session.
        351 * @param {!webdriver.logging.Preferences} prefs The logging preferences.
        352 * @return {!Options} A self reference.
        353 */
        354Options.prototype.setLoggingPreferences = function(prefs) {
        355 this.logPrefs_ = prefs;
        356 return this;
        357};
        358
        359
        360/**
        361 * Sets preferences for the "Local State" file in Chrome's user data
        362 * directory.
        363 * @param {!Object} state Dictionary of local state preferences.
        364 * @return {!Options} A self reference.
        365 */
        366Options.prototype.setLocalState = function(state) {
        367 this.localState_ = state;
        368 return this;
        369};
        370
        371
        372/**
        373 * Sets the path to Chrome's log file. This path should exist on the machine
        374 * that will launch Chrome.
        375 * @param {string} path Path to the log file to use.
        376 * @return {!Options} A self reference.
        377 */
        378Options.prototype.setChromeLogFile = function(path) {
        379 this.logFile_ = path;
        380 return this;
        381};
        382
        383
        384/**
        385 * Sets the proxy settings for the new session.
        386 * @param {ProxyConfig} proxy The proxy configuration to use.
        387 * @return {!Options} A self reference.
        388 */
        389Options.prototype.setProxy = function(proxy) {
        390 this.proxy_ = proxy;
        391 return this;
        392};
        393
        394
        395/**
        396 * Converts this options instance to a {@link webdriver.Capabilities} object.
        397 * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge
        398 * these options into, if any.
        399 * @return {!webdriver.Capabilities} The capabilities.
        400 */
        401Options.prototype.toCapabilities = function(opt_capabilities) {
        402 var capabilities = opt_capabilities || webdriver.Capabilities.chrome();
        403 capabilities.
        404 set(webdriver.Capability.PROXY, this.proxy_).
        405 set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_).
        406 set(OPTIONS_CAPABILITY_KEY, this);
        407 return capabilities;
        408};
        409
        410
        411/**
        412 * Converts this instance to its JSON wire protocol representation. Note this
        413 * function is an implementation not intended for general use.
        414 * @return {{args: !Array.<string>,
        415 * binary: (string|undefined),
        416 * detach: boolean,
        417 * extensions: !Array.<string>,
        418 * localState: (Object|undefined),
        419 * logFile: (string|undefined),
        420 * prefs: (Object|undefined)}} The JSON wire protocol representation
        421 * of this instance.
        422 */
        423Options.prototype.toJSON = function() {
        424 return {
        425 args: this.args_,
        426 binary: this.binary_,
        427 detach: !!this.detach_,
        428 extensions: this.extensions_.map(function(extension) {
        429 if (Buffer.isBuffer(extension)) {
        430 return extension.toString('base64');
        431 }
        432 return fs.readFileSync(extension, 'base64');
        433 }),
        434 localState: this.localState_,
        435 logFile: this.logFile_,
        436 prefs: this.prefs_
        437 };
        438};
        439
        440
        441/**
        442 * Creates a new ChromeDriver session.
        443 * @param {(webdriver.Capabilities|Options)=} opt_options The session options.
        444 * @param {remote.DriverService=} opt_service The session to use; will use
        445 * the {@link getDefaultService default service} by default.
        446 * @return {!webdriver.WebDriver} A new WebDriver instance.
        447 */
        448function createDriver(opt_options, opt_service) {
        449 var service = opt_service || getDefaultService();
        450 var executor = executors.createExecutor(service.start());
        451
        452 var options = opt_options || new Options();
        453 if (opt_options instanceof webdriver.Capabilities) {
        454 // Extract the Chrome-specific options so we do not send unnecessary
        455 // data across the wire.
        456 options = Options.fromCapabilities(options);
        457 }
        458
        459 return webdriver.WebDriver.createSession(
        460 executor, options.toCapabilities());
        461}
        462
        463
        464
        465// PUBLIC API
        466
        467
        468exports.ServiceBuilder = ServiceBuilder;
        469exports.Options = Options;
        470exports.createDriver = createDriver;
        471exports.getDefaultService = getDefaultService;
        472exports.setDefaultService = setDefaultService;
        \ No newline at end of file +chrome.js

        chrome.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines a {@linkplain Driver WebDriver} client for the Chrome
        20 * web browser. Before using this module, you must download the latest
        21 * [ChromeDriver release] and ensure it can be found on your system [PATH].
        22 *
        23 * There are three primary classes exported by this module:
        24 *
        25 * 1. {@linkplain ServiceBuilder}: configures the
        26 * {@link selenium-webdriver/remote.DriverService remote.DriverService}
        27 * that manages the [ChromeDriver] child process.
        28 *
        29 * 2. {@linkplain Options}: defines configuration options for each new Chrome
        30 * session, such as which {@linkplain Options#setProxy proxy} to use,
        31 * what {@linkplain Options#addExtensions extensions} to install, or
        32 * what {@linkplain Options#addArguments command-line switches} to use when
        33 * starting the browser.
        34 *
        35 * 3. {@linkplain Driver}: the WebDriver client; each new instance will control
        36 * a unique browser session with a clean user profile (unless otherwise
        37 * configured through the {@link Options} class).
        38 *
        39 * __Customizing the ChromeDriver Server__ <a id="custom-server"></a>
        40 *
        41 * By default, every Chrome session will use a single driver service, which is
        42 * started the first time a {@link Driver} instance is created and terminated
        43 * when this process exits. The default service will inherit its environment
        44 * from the current process and direct all output to /dev/null. You may obtain
        45 * a handle to this default service using
        46 * {@link #getDefaultService getDefaultService()} and change its configuration
        47 * with {@link #setDefaultService setDefaultService()}.
        48 *
        49 * You may also create a {@link Driver} with its own driver service. This is
        50 * useful if you need to capture the server's log output for a specific session:
        51 *
        52 * var chrome = require('selenium-webdriver/chrome');
        53 *
        54 * var service = new chrome.ServiceBuilder()
        55 * .loggingTo('/my/log/file.txt')
        56 * .enableVerboseLogging()
        57 * .build();
        58 *
        59 * var options = new chrome.Options();
        60 * // configure browser options ...
        61 *
        62 * var driver = new chrome.Driver(options, service);
        63 *
        64 * Users should only instantiate the {@link Driver} class directly when they
        65 * need a custom driver service configuration (as shown above). For normal
        66 * operation, users should start Chrome using the
        67 * {@link selenium-webdriver.Builder}.
        68 *
        69 * __Working with Android__ <a id="android"></a>
        70 *
        71 * The [ChromeDriver][android] supports running tests on the Chrome browser as
        72 * well as [WebView apps][webview] starting in Android 4.4 (KitKat). In order to
        73 * work with Android, you must first start the adb
        74 *
        75 * adb start-server
        76 *
        77 * By default, adb will start on port 5037. You may change this port, but this
        78 * will require configuring a [custom server](#custom-server) that will connect
        79 * to adb on the {@linkplain ServiceBuilder#setAdbPort correct port}:
        80 *
        81 * var service = new chrome.ServiceBuilder()
        82 * .setAdbPort(1234)
        83 * build();
        84 * // etc.
        85 *
        86 * The ChromeDriver may be configured to launch Chrome on Android using
        87 * {@link Options#androidChrome()}:
        88 *
        89 * var driver = new Builder()
        90 * .forBrowser('chrome')
        91 * .setChromeOptions(new chrome.Options().androidChrome())
        92 * .build();
        93 *
        94 * Alternatively, you can configure the ChromeDriver to launch an app with a
        95 * Chrome-WebView by setting the {@linkplain Options#androidActivity
        96 * androidActivity} option:
        97 *
        98 * var driver = new Builder()
        99 * .forBrowser('chrome')
        100 * .setChromeOptions(new chrome.Options()
        101 * .androidPackage('com.example')
        102 * .androidActivity('com.example.Activity'))
        103 * .build();
        104 *
        105 * [Refer to the ChromeDriver site] for more information on using the
        106 * [ChromeDriver with Android][android].
        107 *
        108 * [ChromeDriver]: https://sites.google.com/a/chromium.org/chromedriver/
        109 * [ChromeDriver release]: http://chromedriver.storage.googleapis.com/index.html
        110 * [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
        111 * [android]: https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android
        112 * [webview]: https://developer.chrome.com/multidevice/webview/overview
        113 */
        114
        115'use strict';
        116
        117var fs = require('fs'),
        118 util = require('util');
        119
        120var webdriver = require('./index'),
        121 executors = require('./executors'),
        122 http = require('./http'),
        123 io = require('./io'),
        124 portprober = require('./net/portprober'),
        125 remote = require('./remote');
        126
        127
        128/**
        129 * Name of the ChromeDriver executable.
        130 * @type {string}
        131 * @const
        132 */
        133var CHROMEDRIVER_EXE =
        134 process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver';
        135
        136
        137/**
        138 * Custom command names supported by ChromeDriver.
        139 * @enum {string}
        140 */
        141var Command = {
        142 LAUNCH_APP: 'launchApp'
        143};
        144
        145
        146/**
        147 * Creates a command executor with support for ChromeDriver's custom commands.
        148 * @param {!webdriver.promise.Promise<string>} url The server's URL.
        149 * @return {!webdriver.CommandExecutor} The new command executor.
        150 */
        151function createExecutor(url) {
        152 return new executors.DeferredExecutor(url.then(function(url) {
        153 var client = new http.HttpClient(url);
        154 var executor = new http.Executor(client);
        155 executor.defineCommand(
        156 Command.LAUNCH_APP,
        157 'POST', '/session/:sessionId/chromium/launch_app');
        158 return executor;
        159 }));
        160}
        161
        162
        163/**
        164 * Creates {@link selenium-webdriver/remote.DriverService} instances that manage
        165 * a [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/)
        166 * server in a child process.
        167 *
        168 * @param {string=} opt_exe Path to the server executable to use. If omitted,
        169 * the builder will attempt to locate the chromedriver on the current
        170 * PATH.
        171 * @throws {Error} If provided executable does not exist, or the chromedriver
        172 * cannot be found on the PATH.
        173 * @constructor
        174 */
        175var ServiceBuilder = function(opt_exe) {
        176 /** @private {string} */
        177 this.exe_ = opt_exe || io.findInPath(CHROMEDRIVER_EXE, true);
        178 if (!this.exe_) {
        179 throw Error(
        180 'The ChromeDriver could not be found on the current PATH. Please ' +
        181 'download the latest version of the ChromeDriver from ' +
        182 'http://chromedriver.storage.googleapis.com/index.html and ensure ' +
        183 'it can be found on your PATH.');
        184 }
        185
        186 if (!fs.existsSync(this.exe_)) {
        187 throw Error('File does not exist: ' + this.exe_);
        188 }
        189
        190 /** @private {!Array.<string>} */
        191 this.args_ = [];
        192 this.stdio_ = 'ignore';
        193};
        194
        195
        196/** @private {string} */
        197ServiceBuilder.prototype.path_ = null;
        198
        199/** @private {number} */
        200ServiceBuilder.prototype.port_ = 0;
        201
        202
        203/** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
        204ServiceBuilder.prototype.stdio_ = 'ignore';
        205
        206
        207/** @private {Object.<string, string>} */
        208ServiceBuilder.prototype.env_ = null;
        209
        210
        211/**
        212 * Sets the port to start the ChromeDriver on.
        213 * @param {number} port The port to use, or 0 for any free port.
        214 * @return {!ServiceBuilder} A self reference.
        215 * @throws {Error} If the port is invalid.
        216 */
        217ServiceBuilder.prototype.usingPort = function(port) {
        218 if (port < 0) {
        219 throw Error('port must be >= 0: ' + port);
        220 }
        221 this.port_ = port;
        222 return this;
        223};
        224
        225
        226/**
        227 * Sets which port adb is listening to. _The ChromeDriver will connect to adb
        228 * if an {@linkplain Options#androidPackage Android session} is requested, but
        229 * adb **must** be started beforehand._
        230 *
        231 * @param {number} port Which port adb is running on.
        232 * @return {!ServiceBuilder} A self reference.
        233 */
        234ServiceBuilder.prototype.setAdbPort = function(port) {
        235 this.args_.push('--adb-port=' + port);
        236 return this;
        237};
        238
        239
        240/**
        241 * Sets the path of the log file the driver should log to. If a log file is
        242 * not specified, the driver will log to stderr.
        243 * @param {string} path Path of the log file to use.
        244 * @return {!ServiceBuilder} A self reference.
        245 */
        246ServiceBuilder.prototype.loggingTo = function(path) {
        247 this.args_.push('--log-path=' + path);
        248 return this;
        249};
        250
        251
        252/**
        253 * Enables verbose logging.
        254 * @return {!ServiceBuilder} A self reference.
        255 */
        256ServiceBuilder.prototype.enableVerboseLogging = function() {
        257 this.args_.push('--verbose');
        258 return this;
        259};
        260
        261
        262/**
        263 * Sets the number of threads the driver should use to manage HTTP requests.
        264 * By default, the driver will use 4 threads.
        265 * @param {number} n The number of threads to use.
        266 * @return {!ServiceBuilder} A self reference.
        267 */
        268ServiceBuilder.prototype.setNumHttpThreads = function(n) {
        269 this.args_.push('--http-threads=' + n);
        270 return this;
        271};
        272
        273
        274/**
        275 * Sets the base path for WebDriver REST commands (e.g. "/wd/hub").
        276 * By default, the driver will accept commands relative to "/".
        277 * @param {string} path The base path to use.
        278 * @return {!ServiceBuilder} A self reference.
        279 */
        280ServiceBuilder.prototype.setUrlBasePath = function(path) {
        281 this.args_.push('--url-base=' + path);
        282 this.path_ = path;
        283 return this;
        284};
        285
        286
        287/**
        288 * Defines the stdio configuration for the driver service. See
        289 * {@code child_process.spawn} for more information.
        290 * @param {(string|!Array.<string|number|!Stream|null|undefined>)} config The
        291 * configuration to use.
        292 * @return {!ServiceBuilder} A self reference.
        293 */
        294ServiceBuilder.prototype.setStdio = function(config) {
        295 this.stdio_ = config;
        296 return this;
        297};
        298
        299
        300/**
        301 * Defines the environment to start the server under. This settings will be
        302 * inherited by every browser session started by the server.
        303 * @param {!Object.<string, string>} env The environment to use.
        304 * @return {!ServiceBuilder} A self reference.
        305 */
        306ServiceBuilder.prototype.withEnvironment = function(env) {
        307 this.env_ = env;
        308 return this;
        309};
        310
        311
        312/**
        313 * Creates a new DriverService using this instance's current configuration.
        314 * @return {remote.DriverService} A new driver service using this instance's
        315 * current configuration.
        316 * @throws {Error} If the driver exectuable was not specified and a default
        317 * could not be found on the current PATH.
        318 */
        319ServiceBuilder.prototype.build = function() {
        320 var port = this.port_ || portprober.findFreePort();
        321 var args = this.args_.concat(); // Defensive copy.
        322
        323 return new remote.DriverService(this.exe_, {
        324 loopback: true,
        325 path: this.path_,
        326 port: port,
        327 args: webdriver.promise.when(port, function(port) {
        328 return args.concat('--port=' + port);
        329 }),
        330 env: this.env_,
        331 stdio: this.stdio_
        332 });
        333};
        334
        335
        336/** @type {remote.DriverService} */
        337var defaultService = null;
        338
        339
        340/**
        341 * Sets the default service to use for new ChromeDriver instances.
        342 * @param {!remote.DriverService} service The service to use.
        343 * @throws {Error} If the default service is currently running.
        344 */
        345function setDefaultService(service) {
        346 if (defaultService && defaultService.isRunning()) {
        347 throw Error(
        348 'The previously configured ChromeDriver service is still running. ' +
        349 'You must shut it down before you may adjust its configuration.');
        350 }
        351 defaultService = service;
        352}
        353
        354
        355/**
        356 * Returns the default ChromeDriver service. If such a service has not been
        357 * configured, one will be constructed using the default configuration for
        358 * a ChromeDriver executable found on the system PATH.
        359 * @return {!remote.DriverService} The default ChromeDriver service.
        360 */
        361function getDefaultService() {
        362 if (!defaultService) {
        363 defaultService = new ServiceBuilder().build();
        364 }
        365 return defaultService;
        366}
        367
        368
        369/**
        370 * @type {string}
        371 * @const
        372 */
        373var OPTIONS_CAPABILITY_KEY = 'chromeOptions';
        374
        375
        376/**
        377 * Class for managing ChromeDriver specific options.
        378 * @constructor
        379 * @extends {webdriver.Serializable}
        380 */
        381var Options = function() {
        382 webdriver.Serializable.call(this);
        383
        384 /** @private {!Object} */
        385 this.options_ = {};
        386
        387 /** @private {!Array.<(string|!Buffer)>} */
        388 this.extensions_ = [];
        389
        390 /** @private {?webdriver.logging.Preferences} */
        391 this.logPrefs_ = null;
        392
        393 /** @private {?webdriver.ProxyConfig} */
        394 this.proxy_ = null;
        395};
        396util.inherits(Options, webdriver.Serializable);
        397
        398
        399/**
        400 * Extracts the ChromeDriver specific options from the given capabilities
        401 * object.
        402 * @param {!webdriver.Capabilities} capabilities The capabilities object.
        403 * @return {!Options} The ChromeDriver options.
        404 */
        405Options.fromCapabilities = function(capabilities) {
        406 var options = new Options();
        407
        408 var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
        409 if (o instanceof Options) {
        410 options = o;
        411 } else if (o) {
        412 options.
        413 addArguments(o.args || []).
        414 addExtensions(o.extensions || []).
        415 detachDriver(o.detach).
        416 excludeSwitches(o.excludeSwitches || []).
        417 setChromeBinaryPath(o.binary).
        418 setChromeLogFile(o.logPath).
        419 setChromeMinidumpPath(o.minidumpPath).
        420 setLocalState(o.localState).
        421 setMobileEmulation(o.mobileEmulation).
        422 setUserPreferences(o.prefs).
        423 setPerfLoggingPrefs(o.perfLoggingPrefs);
        424 }
        425
        426 if (capabilities.has(webdriver.Capability.PROXY)) {
        427 options.setProxy(capabilities.get(webdriver.Capability.PROXY));
        428 }
        429
        430 if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) {
        431 options.setLoggingPrefs(
        432 capabilities.get(webdriver.Capability.LOGGING_PREFS));
        433 }
        434
        435 return options;
        436};
        437
        438
        439/**
        440 * Add additional command line arguments to use when launching the Chrome
        441 * browser. Each argument may be specified with or without the "--" prefix
        442 * (e.g. "--foo" and "foo"). Arguments with an associated value should be
        443 * delimited by an "=": "foo=bar".
        444 * @param {...(string|!Array.<string>)} var_args The arguments to add.
        445 * @return {!Options} A self reference.
        446 */
        447Options.prototype.addArguments = function(var_args) {
        448 var args = this.options_.args || [];
        449 args = args.concat.apply(args, arguments);
        450 if (args.length) {
        451 this.options_.args = args;
        452 }
        453 return this;
        454};
        455
        456
        457/**
        458 * List of Chrome command line switches to exclude that ChromeDriver by default
        459 * passes when starting Chrome. Do not prefix switches with "--".
        460 *
        461 * @param {...(string|!Array<string>)} var_args The switches to exclude.
        462 * @return {!Options} A self reference.
        463 */
        464Options.prototype.excludeSwitches = function(var_args) {
        465 var switches = this.options_.excludeSwitches || [];
        466 switches = switches.concat.apply(switches, arguments);
        467 if (switches.length) {
        468 this.options_.excludeSwitches = switches;
        469 }
        470 return this;
        471};
        472
        473
        474/**
        475 * Add additional extensions to install when launching Chrome. Each extension
        476 * should be specified as the path to the packed CRX file, or a Buffer for an
        477 * extension.
        478 * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The
        479 * extensions to add.
        480 * @return {!Options} A self reference.
        481 */
        482Options.prototype.addExtensions = function(var_args) {
        483 this.extensions_ = this.extensions_.concat.apply(this.extensions_, arguments);
        484 return this;
        485};
        486
        487
        488/**
        489 * Sets the path to the Chrome binary to use. On Mac OS X, this path should
        490 * reference the actual Chrome executable, not just the application binary
        491 * (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
        492 *
        493 * The binary path be absolute or relative to the chromedriver server
        494 * executable, but it must exist on the machine that will launch Chrome.
        495 *
        496 * @param {string} path The path to the Chrome binary to use.
        497 * @return {!Options} A self reference.
        498 */
        499Options.prototype.setChromeBinaryPath = function(path) {
        500 this.options_.binary = path;
        501 return this;
        502};
        503
        504
        505/**
        506 * Sets whether to leave the started Chrome browser running if the controlling
        507 * ChromeDriver service is killed before {@link webdriver.WebDriver#quit()} is
        508 * called.
        509 * @param {boolean} detach Whether to leave the browser running if the
        510 * chromedriver service is killed before the session.
        511 * @return {!Options} A self reference.
        512 */
        513Options.prototype.detachDriver = function(detach) {
        514 this.options_.detach = detach;
        515 return this;
        516};
        517
        518
        519/**
        520 * Sets the user preferences for Chrome's user profile. See the "Preferences"
        521 * file in Chrome's user data directory for examples.
        522 * @param {!Object} prefs Dictionary of user preferences to use.
        523 * @return {!Options} A self reference.
        524 */
        525Options.prototype.setUserPreferences = function(prefs) {
        526 this.options_.prefs = prefs;
        527 return this;
        528};
        529
        530
        531/**
        532 * Sets the logging preferences for the new session.
        533 * @param {!webdriver.logging.Preferences} prefs The logging preferences.
        534 * @return {!Options} A self reference.
        535 */
        536Options.prototype.setLoggingPrefs = function(prefs) {
        537 this.logPrefs_ = prefs;
        538 return this;
        539};
        540
        541
        542/**
        543 * Sets the performance logging preferences. Options include:
        544 *
        545 * - `enableNetwork`: Whether or not to collect events from Network domain.
        546 * - `enablePage`: Whether or not to collect events from Page domain.
        547 * - `enableTimeline`: Whether or not to collect events from Timeline domain.
        548 * Note: when tracing is enabled, Timeline domain is implicitly disabled,
        549 * unless `enableTimeline` is explicitly set to true.
        550 * - `tracingCategories`: A comma-separated string of Chrome tracing categories
        551 * for which trace events should be collected. An unspecified or empty
        552 * string disables tracing.
        553 * - `bufferUsageReportingInterval`: The requested number of milliseconds
        554 * between DevTools trace buffer usage events. For example, if 1000, then
        555 * once per second, DevTools will report how full the trace buffer is. If a
        556 * report indicates the buffer usage is 100%, a warning will be issued.
        557 *
        558 * @param {{enableNetwork: boolean,
        559 * enablePage: boolean,
        560 * enableTimeline: boolean,
        561 * tracingCategories: string,
        562 * bufferUsageReportingInterval: number}} prefs The performance
        563 * logging preferences.
        564 * @return {!Options} A self reference.
        565 */
        566Options.prototype.setPerfLoggingPrefs = function(prefs) {
        567 this.options_.perfLoggingPrefs = prefs;
        568 return this;
        569};
        570
        571
        572/**
        573 * Sets preferences for the "Local State" file in Chrome's user data
        574 * directory.
        575 * @param {!Object} state Dictionary of local state preferences.
        576 * @return {!Options} A self reference.
        577 */
        578Options.prototype.setLocalState = function(state) {
        579 this.options_.localState = state;
        580 return this;
        581};
        582
        583
        584/**
        585 * Sets the name of the activity hosting a Chrome-based Android WebView. This
        586 * option must be set to connect to an [Android WebView](
        587 * https://sites.google.com/a/chromium.org/chromedriver/getting-started/getting-started---android)
        588 *
        589 * @param {string} name The activity name.
        590 * @return {!Options} A self reference.
        591 */
        592Options.prototype.androidActivity = function(name) {
        593 this.options_.androidActivity = name;
        594 return this;
        595};
        596
        597
        598/**
        599 * Sets the device serial number to connect to via ADB. If not specified, the
        600 * ChromeDriver will select an unused device at random. An error will be
        601 * returned if all devices already have active sessions.
        602 *
        603 * @param {string} serial The device serial number to connect to.
        604 * @return {!Options} A self reference.
        605 */
        606Options.prototype.androidDeviceSerial = function(serial) {
        607 this.options_.androidDeviceSerial = serial;
        608 return this;
        609};
        610
        611
        612/**
        613 * Configures the ChromeDriver to launch Chrome on Android via adb. This
        614 * function is shorthand for
        615 * {@link #androidPackage options.androidPackage('com.android.chrome')}.
        616 * @return {!Options} A self reference.
        617 */
        618Options.prototype.androidChrome = function() {
        619 return this.androidPackage('com.android.chrome');
        620};
        621
        622
        623/**
        624 * Sets the package name of the Chrome or WebView app.
        625 *
        626 * @param {?string} pkg The package to connect to, or `null` to disable Android
        627 * and switch back to using desktop Chrome.
        628 * @return {!Options} A self reference.
        629 */
        630Options.prototype.androidPackage = function(pkg) {
        631 this.options_.androidPackage = pkg;
        632 return this;
        633};
        634
        635
        636/**
        637 * Sets the process name of the Activity hosting the WebView (as given by `ps`).
        638 * If not specified, the process name is assumed to be the same as
        639 * {@link #androidPackage}.
        640 *
        641 * @param {string} processName The main activity name.
        642 * @return {!Options} A self reference.
        643 */
        644Options.prototype.androidProcess = function(processName) {
        645 this.options_.androidProcess = processName;
        646 return this;
        647};
        648
        649
        650/**
        651 * Sets whether to connect to an already-running instead of the specified
        652 * {@linkplain #androidProcess app} instead of launching the app with a clean
        653 * data directory.
        654 *
        655 * @param {boolean} useRunning Whether to connect to a running instance.
        656 * @return {!Options} A self reference.
        657 */
        658Options.prototype.androidUseRunningApp = function(useRunning) {
        659 this.options_.androidUseRunningApp = useRunning;
        660 return this;
        661};
        662
        663
        664/**
        665 * Sets the path to Chrome's log file. This path should exist on the machine
        666 * that will launch Chrome.
        667 * @param {string} path Path to the log file to use.
        668 * @return {!Options} A self reference.
        669 */
        670Options.prototype.setChromeLogFile = function(path) {
        671 this.options_.logPath = path;
        672 return this;
        673};
        674
        675
        676/**
        677 * Sets the directory to store Chrome minidumps in. This option is only
        678 * supported when ChromeDriver is running on Linux.
        679 * @param {string} path The directory path.
        680 * @return {!Options} A self reference.
        681 */
        682Options.prototype.setChromeMinidumpPath = function(path) {
        683 this.options_.minidumpPath = path;
        684 return this;
        685};
        686
        687
        688/**
        689 * Configures Chrome to emulate a mobile device. For more information, refer to
        690 * the ChromeDriver project page on [mobile emulation][em]. Configuration
        691 * options include:
        692 *
        693 * - `deviceName`: The name of a pre-configured [emulated device][devem]
        694 * - `width`: screen width, in pixels
        695 * - `height`: screen height, in pixels
        696 * - `pixelRatio`: screen pixel ratio
        697 *
        698 * __Example 1: Using a Pre-configured Device__
        699 *
        700 * var options = new chrome.Options().setMobileEmulation(
        701 * {deviceName: 'Google Nexus 5'});
        702 *
        703 * var driver = new chrome.Driver(options);
        704 *
        705 * __Example 2: Using Custom Screen Configuration__
        706 *
        707 * var options = new chrome.Options().setMobileEmulation({
        708 * width: 360,
        709 * height: 640,
        710 * pixelRatio: 3.0
        711 * });
        712 *
        713 * var driver = new chrome.Driver(options);
        714 *
        715 *
        716 * [em]: https://sites.google.com/a/chromium.org/chromedriver/mobile-emulation
        717 * [devem]: https://developer.chrome.com/devtools/docs/device-mode
        718 *
        719 * @param {?({deviceName: string}|
        720 * {width: number, height: number, pixelRatio: number})} config The
        721 * mobile emulation configuration, or `null` to disable emulation.
        722 * @return {!Options} A self reference.
        723 */
        724Options.prototype.setMobileEmulation = function(config) {
        725 this.options_.mobileEmulation = config;
        726 return this;
        727};
        728
        729
        730/**
        731 * Sets the proxy settings for the new session.
        732 * @param {webdriver.ProxyConfig} proxy The proxy configuration to use.
        733 * @return {!Options} A self reference.
        734 */
        735Options.prototype.setProxy = function(proxy) {
        736 this.proxy_ = proxy;
        737 return this;
        738};
        739
        740
        741/**
        742 * Converts this options instance to a {@link webdriver.Capabilities} object.
        743 * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge
        744 * these options into, if any.
        745 * @return {!webdriver.Capabilities} The capabilities.
        746 */
        747Options.prototype.toCapabilities = function(opt_capabilities) {
        748 var capabilities = opt_capabilities || webdriver.Capabilities.chrome();
        749 capabilities.
        750 set(webdriver.Capability.PROXY, this.proxy_).
        751 set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_).
        752 set(OPTIONS_CAPABILITY_KEY, this);
        753 return capabilities;
        754};
        755
        756
        757/**
        758 * Converts this instance to its JSON wire protocol representation. Note this
        759 * function is an implementation not intended for general use.
        760 * @return {{args: !Array.<string>,
        761 * binary: (string|undefined),
        762 * detach: boolean,
        763 * extensions: !Array.<(string|!webdriver.promise.Promise.<string>)>,
        764 * localState: (Object|undefined),
        765 * logPath: (string|undefined),
        766 * prefs: (Object|undefined)}} The JSON wire protocol representation
        767 * of this instance.
        768 * @override
        769 */
        770Options.prototype.serialize = function() {
        771 var json = {};
        772 for (var key in this.options_) {
        773 if (this.options_[key] != null) {
        774 json[key] = this.options_[key];
        775 }
        776 }
        777 if (this.extensions_.length) {
        778 json.extensions = this.extensions_.map(function(extension) {
        779 if (Buffer.isBuffer(extension)) {
        780 return extension.toString('base64');
        781 }
        782 return webdriver.promise.checkedNodeCall(
        783 fs.readFile, extension, 'base64');
        784 });
        785 }
        786 return json;
        787};
        788
        789
        790/**
        791 * Creates a new WebDriver client for Chrome.
        792 *
        793 * @param {(webdriver.Capabilities|Options)=} opt_config The configuration
        794 * options.
        795 * @param {remote.DriverService=} opt_service The session to use; will use
        796 * the {@linkplain #getDefaultService default service} by default.
        797 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
        798 * {@code null} to use the currently active flow.
        799 * @constructor
        800 * @extends {webdriver.WebDriver}
        801 */
        802var Driver = function(opt_config, opt_service, opt_flow) {
        803 var service = opt_service || getDefaultService();
        804 var executor = createExecutor(service.start());
        805
        806 var capabilities =
        807 opt_config instanceof Options ? opt_config.toCapabilities() :
        808 (opt_config || webdriver.Capabilities.chrome());
        809
        810 var driver = webdriver.WebDriver.createSession(
        811 executor, capabilities, opt_flow);
        812
        813 webdriver.WebDriver.call(
        814 this, driver.getSession(), executor, driver.controlFlow());
        815};
        816util.inherits(Driver, webdriver.WebDriver);
        817
        818
        819/**
        820 * This function is a no-op as file detectors are not supported by this
        821 * implementation.
        822 * @override
        823 */
        824Driver.prototype.setFileDetector = function() {
        825};
        826
        827
        828/**
        829 * Schedules a command to launch Chrome App with given ID.
        830 * @param {string} id ID of the App to launch.
        831 * @return {!webdriver.promise.Promise<void>} A promise that will be resolved
        832 * when app is launched.
        833 */
        834Driver.prototype.launchApp = function(id) {
        835 return this.schedule(
        836 new webdriver.Command(Command.LAUNCH_APP).setParameter('id', id),
        837 'Driver.launchApp()');
        838};
        839
        840
        841// PUBLIC API
        842
        843
        844exports.Driver = Driver;
        845exports.Options = Options;
        846exports.ServiceBuilder = ServiceBuilder;
        847exports.getDefaultService = getDefaultService;
        848exports.setDefaultService = setDefaultService;
        \ No newline at end of file diff --git a/docs/source/error.js.src.html b/docs/source/error.js.src.html index a8aa5b0..89a1a72 100644 --- a/docs/source/error.js.src.html +++ b/docs/source/error.js.src.html @@ -1 +1 @@ -error.js

        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
        18var base = require('./_base');
        19
        20
        21/** @type {function(new: bot.Error)} */
        22exports.Error = base.require('bot.Error');
        23
        24/** @type {bot.ErrorCode.} */
        25exports.ErrorCode = base.require('bot.ErrorCode');
        \ No newline at end of file +error.js

        error.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18'use strict';
        19
        20var base = require('./_base');
        21
        22
        23/** @type {function(new: bot.Error)} */
        24exports.Error = base.require('bot.Error');
        25
        26/** @type {bot.ErrorCode.} */
        27exports.ErrorCode = base.require('bot.ErrorCode');
        \ No newline at end of file diff --git a/docs/source/executors.js.src.html b/docs/source/executors.js.src.html index b44c5ad..27312a2 100644 --- a/docs/source/executors.js.src.html +++ b/docs/source/executors.js.src.html @@ -1 +1 @@ -executors.js

        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
        20var 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 */
        34var 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 */
        54exports.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};
        \ No newline at end of file +executors.js

        executors.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Various utilities for working with
        20 * {@link webdriver.CommandExecutor} implementations.
        21 */
        22
        23var HttpClient = require('./http').HttpClient,
        24 HttpExecutor = require('./http').Executor,
        25 promise = require('./_base').require('webdriver.promise');
        26
        27
        28
        29/**
        30 * Wraps a promised {@link webdriver.CommandExecutor}, ensuring no commands
        31 * are executed until the wrapped executor has been fully built.
        32 * @param {!webdriver.promise.Promise.<!webdriver.CommandExecutor>} delegate The
        33 * promised delegate.
        34 * @constructor
        35 * @implements {webdriver.CommandExecutor}
        36 */
        37var DeferredExecutor = function(delegate) {
        38
        39 /** @override */
        40 this.execute = function(command, callback) {
        41 delegate.then(function(executor) {
        42 executor.execute(command, callback);
        43 }, callback);
        44 };
        45};
        46
        47
        48// PUBLIC API
        49
        50
        51exports.DeferredExecutor = DeferredExecutor;
        52
        53/**
        54 * Creates a command executor that uses WebDriver's JSON wire protocol.
        55 * @param {(string|!webdriver.promise.Promise.<string>)} url The server's URL,
        56 * or a promise that will resolve to that URL.
        57 * @param {string=} opt_proxy (optional) The URL of the HTTP proxy for the
        58 * client to use.
        59 * @returns {!webdriver.CommandExecutor} The new command executor.
        60 */
        61exports.createExecutor = function(url, opt_proxy) {
        62 return new DeferredExecutor(promise.when(url, function(url) {
        63 var client = new HttpClient(url, null, opt_proxy);
        64 return new HttpExecutor(client);
        65 }));
        66};
        \ No newline at end of file diff --git a/docs/source/firefox/binary.js.src.html b/docs/source/firefox/binary.js.src.html new file mode 100644 index 0000000..9f5542a --- /dev/null +++ b/docs/source/firefox/binary.js.src.html @@ -0,0 +1 @@ +binary.js

        firefox/binary.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Manages Firefox binaries. This module is considered internal;
        20 * users should use {@link selenium-webdriver/firefox}.
        21 */
        22
        23'use strict';
        24
        25var child = require('child_process'),
        26 fs = require('fs'),
        27 path = require('path'),
        28 util = require('util');
        29
        30var Serializable = require('..').Serializable,
        31 promise = require('..').promise,
        32 _base = require('../_base'),
        33 io = require('../io'),
        34 exec = require('../io/exec');
        35
        36
        37
        38/** @const */
        39var NO_FOCUS_LIB_X86 = _base.isDevMode() ?
        40 path.join(__dirname, '../../../../cpp/prebuilt/i386/libnoblur.so') :
        41 path.join(__dirname, '../lib/firefox/i386/libnoblur.so') ;
        42
        43/** @const */
        44var NO_FOCUS_LIB_AMD64 = _base.isDevMode() ?
        45 path.join(__dirname, '../../../../cpp/prebuilt/amd64/libnoblur64.so') :
        46 path.join(__dirname, '../lib/firefox/amd64/libnoblur64.so') ;
        47
        48var X_IGNORE_NO_FOCUS_LIB = 'x_ignore_nofocus.so';
        49
        50var foundBinary = null;
        51
        52
        53/**
        54 * Checks the default Windows Firefox locations in Program Files.
        55 * @return {!promise.Promise.<?string>} A promise for the located executable.
        56 * The promise will resolve to {@code null} if Fireox was not found.
        57 */
        58function defaultWindowsLocation() {
        59 var files = [
        60 process.env['PROGRAMFILES'] || 'C:\\Program Files',
        61 process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
        62 ].map(function(prefix) {
        63 return path.join(prefix, 'Mozilla Firefox\\firefox.exe');
        64 });
        65 return io.exists(files[0]).then(function(exists) {
        66 return exists ? files[0] : io.exists(files[1]).then(function(exists) {
        67 return exists ? files[1] : null;
        68 });
        69 });
        70}
        71
        72
        73/**
        74 * Locates the Firefox binary for the current system.
        75 * @return {!promise.Promise.<string>} A promise for the located binary. The
        76 * promise will be rejected if Firefox cannot be located.
        77 */
        78function findFirefox() {
        79 if (foundBinary) {
        80 return foundBinary;
        81 }
        82
        83 if (process.platform === 'darwin') {
        84 var osxExe = '/Applications/Firefox.app/Contents/MacOS/firefox-bin';
        85 foundBinary = io.exists(osxExe).then(function(exists) {
        86 return exists ? osxExe : null;
        87 });
        88 } else if (process.platform === 'win32') {
        89 foundBinary = defaultWindowsLocation();
        90 } else {
        91 foundBinary = promise.fulfilled(io.findInPath('firefox'));
        92 }
        93
        94 return foundBinary = foundBinary.then(function(found) {
        95 if (found) {
        96 return found;
        97 }
        98 throw Error('Could not locate Firefox on the current system');
        99 });
        100}
        101
        102
        103/**
        104 * Copies the no focus libs into the given profile directory.
        105 * @param {string} profileDir Path to the profile directory to install into.
        106 * @return {!promise.Promise.<string>} The LD_LIBRARY_PATH prefix string to use
        107 * for the installed libs.
        108 */
        109function installNoFocusLibs(profileDir) {
        110 var x86 = path.join(profileDir, 'x86');
        111 var amd64 = path.join(profileDir, 'amd64');
        112
        113 return mkdir(x86)
        114 .then(copyLib.bind(null, NO_FOCUS_LIB_X86, x86))
        115 .then(mkdir.bind(null, amd64))
        116 .then(copyLib.bind(null, NO_FOCUS_LIB_AMD64, amd64))
        117 .then(function() {
        118 return x86 + ':' + amd64;
        119 });
        120
        121 function mkdir(dir) {
        122 return io.exists(dir).then(function(exists) {
        123 if (!exists) {
        124 return promise.checkedNodeCall(fs.mkdir, dir);
        125 }
        126 });
        127 }
        128
        129 function copyLib(src, dir) {
        130 return io.copy(src, path.join(dir, X_IGNORE_NO_FOCUS_LIB));
        131 }
        132}
        133
        134
        135/**
        136 * Provides a mechanism to configure and launch Firefox in a subprocess for
        137 * use with WebDriver.
        138 *
        139 * @param {string=} opt_exe Path to the Firefox binary to use. If not
        140 * specified, will attempt to locate Firefox on the current system.
        141 * @constructor
        142 * @extends {Serializable.<string>}
        143 */
        144var Binary = function(opt_exe) {
        145 Serializable.call(this);
        146
        147 /** @private {(string|undefined)} */
        148 this.exe_ = opt_exe;
        149
        150 /** @private {!Array.<string>} */
        151 this.args_ = [];
        152
        153 /** @private {!Object.<string, string>} */
        154 this.env_ = {};
        155 Object.keys(process.env).forEach(function(key) {
        156 this.env_[key] = process.env[key];
        157 }.bind(this));
        158 this.env_['MOZ_CRASHREPORTER_DISABLE'] = '1';
        159 this.env_['MOZ_NO_REMOTE'] = '1';
        160 this.env_['NO_EM_RESTART'] = '1';
        161};
        162util.inherits(Binary, Serializable);
        163
        164
        165/**
        166 * Add arguments to the command line used to start Firefox.
        167 * @param {...(string|!Array.<string>)} var_args Either the arguments to add as
        168 * varargs, or the arguments as an array.
        169 */
        170Binary.prototype.addArguments = function(var_args) {
        171 for (var i = 0; i < arguments.length; i++) {
        172 if (util.isArray(arguments[i])) {
        173 this.args_ = this.args_.concat(arguments[i]);
        174 } else {
        175 this.args_.push(arguments[i]);
        176 }
        177 }
        178};
        179
        180
        181/**
        182 * Launches Firefox and returns a promise that will be fulfilled when the
        183 * process terminates.
        184 * @param {string} profile Path to the profile directory to use.
        185 * @return {!promise.Promise.<!exec.Command>} A promise for the handle to the
        186 * started subprocess.
        187 */
        188Binary.prototype.launch = function(profile) {
        189 var env = {};
        190 Object.keys(this.env_).forEach(function(key) {
        191 env[key] = this.env_[key];
        192 }.bind(this));
        193 env['XRE_PROFILE_PATH'] = profile;
        194
        195 var args = ['-foreground'].concat(this.args_);
        196
        197 return promise.when(this.exe_ || findFirefox(), function(firefox) {
        198 if (process.platform === 'win32' || process.platform === 'darwin') {
        199 return exec(firefox, {args: args, env: env});
        200 }
        201 return installNoFocusLibs(profile).then(function(ldLibraryPath) {
        202 env['LD_LIBRARY_PATH'] = ldLibraryPath + ':' + env['LD_LIBRARY_PATH'];
        203 env['LD_PRELOAD'] = X_IGNORE_NO_FOCUS_LIB;
        204 return exec(firefox, {args: args, env: env});
        205 });
        206 });
        207};
        208
        209
        210/**
        211 * Returns a promise for the wire representation of this binary. Note: the
        212 * FirefoxDriver only supports passing the path to the binary executable over
        213 * the wire; all command line arguments and environment variables will be
        214 * discarded.
        215 *
        216 * @return {!promise.Promise.<string>} A promise for this binary's wire
        217 * representation.
        218 * @override
        219 */
        220Binary.prototype.serialize = function() {
        221 return promise.when(this.exe_ || findFirefox());
        222};
        223
        224
        225// PUBLIC API
        226
        227
        228exports.Binary = Binary;
        229
        \ No newline at end of file diff --git a/docs/source/firefox/extension.js.src.html b/docs/source/firefox/extension.js.src.html new file mode 100644 index 0000000..4e4bb30 --- /dev/null +++ b/docs/source/firefox/extension.js.src.html @@ -0,0 +1 @@ +extension.js

        firefox/extension.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/** @fileoverview Utilities for working with Firefox extensions. */
        19
        20'use strict';
        21
        22var AdmZip = require('adm-zip'),
        23 fs = require('fs'),
        24 path = require('path'),
        25 util = require('util'),
        26 xml = require('xml2js');
        27
        28var promise = require('..').promise,
        29 checkedCall = promise.checkedNodeCall,
        30 io = require('../io');
        31
        32
        33/**
        34 * Thrown when there an add-on is malformed.
        35 * @param {string} msg The error message.
        36 * @constructor
        37 * @extends {Error}
        38 */
        39function AddonFormatError(msg) {
        40 Error.call(this);
        41
        42 Error.captureStackTrace(this, AddonFormatError);
        43
        44 /** @override */
        45 this.name = AddonFormatError.name;
        46
        47 /** @override */
        48 this.message = msg;
        49}
        50util.inherits(AddonFormatError, Error);
        51
        52
        53
        54/**
        55 * Installs an extension to the given directory.
        56 * @param {string} extension Path to the extension to install, as either a xpi
        57 * file or a directory.
        58 * @param {string} dir Path to the directory to install the extension in.
        59 * @return {!promise.Promise.<string>} A promise for the add-on ID once
        60 * installed.
        61 */
        62function install(extension, dir) {
        63 return getDetails(extension).then(function(details) {
        64 function returnId() { return details.id; }
        65
        66 var dst = path.join(dir, details.id);
        67 if (extension.slice(-4) === '.xpi') {
        68 if (!details.unpack) {
        69 return io.copy(extension, dst + '.xpi').then(returnId);
        70 } else {
        71 return checkedCall(fs.readFile, extension).then(function(buff) {
        72 // TODO: find an async library for inflating a zip archive.
        73 new AdmZip(buff).extractAllTo(dst, true);
        74 }).then(returnId);
        75 }
        76 } else {
        77 return io.copyDir(extension, dst).then(returnId);
        78 }
        79 });
        80}
        81
        82
        83/**
        84 * Describes a Firefox add-on.
        85 * @typedef {{id: string, name: string, version: string, unpack: boolean}}
        86 */
        87var AddonDetails;
        88
        89
        90/**
        91 * Extracts the details needed to install an add-on.
        92 * @param {string} addonPath Path to the extension directory.
        93 * @return {!promise.Promise.<!AddonDetails>} A promise for the add-on details.
        94 */
        95function getDetails(addonPath) {
        96 return readManifest(addonPath).then(function(doc) {
        97 var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#');
        98 var rdf = getNamespaceId(
        99 doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
        100
        101 var description = doc[rdf + 'RDF'][rdf + 'Description'][0];
        102 var details = {
        103 id: getNodeText(description, em + 'id'),
        104 name: getNodeText(description, em + 'name'),
        105 version: getNodeText(description, em + 'version'),
        106 unpack: getNodeText(description, em + 'unpack') || false
        107 };
        108
        109 if (typeof details.unpack === 'string') {
        110 details.unpack = details.unpack.toLowerCase() === 'true';
        111 }
        112
        113 if (!details.id) {
        114 throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
        115 }
        116
        117 return details;
        118 });
        119
        120 function getNodeText(node, name) {
        121 return node[name] && node[name][0] || '';
        122 }
        123
        124 function getNamespaceId(doc, url) {
        125 var keys = Object.keys(doc);
        126 if (keys.length !== 1) {
        127 throw new AddonFormatError('Malformed manifest for add-on ' + addonPath);
        128 }
        129
        130 var namespaces = doc[keys[0]].$;
        131 var id = '';
        132 Object.keys(namespaces).some(function(ns) {
        133 if (namespaces[ns] !== url) {
        134 return false;
        135 }
        136
        137 if (ns.indexOf(':') != -1) {
        138 id = ns.split(':')[1] + ':';
        139 }
        140 return true;
        141 });
        142 return id;
        143 }
        144}
        145
        146
        147/**
        148 * Reads the manifest for a Firefox add-on.
        149 * @param {string} addonPath Path to a Firefox add-on as a xpi or an extension.
        150 * @return {!promise.Promise.<!Object>} A promise for the parsed manifest.
        151 */
        152function readManifest(addonPath) {
        153 var manifest;
        154
        155 if (addonPath.slice(-4) === '.xpi') {
        156 manifest = checkedCall(fs.readFile, addonPath).then(function(buff) {
        157 var zip = new AdmZip(buff);
        158 if (!zip.getEntry('install.rdf')) {
        159 throw new AddonFormatError(
        160 'Could not find install.rdf in ' + addonPath);
        161 }
        162 var done = promise.defer();
        163 zip.readAsTextAsync('install.rdf', done.fulfill);
        164 return done.promise;
        165 });
        166 } else {
        167 manifest = checkedCall(fs.stat, addonPath).then(function(stats) {
        168 if (!stats.isDirectory()) {
        169 throw Error(
        170 'Add-on path is niether a xpi nor a directory: ' + addonPath);
        171 }
        172 return checkedCall(fs.readFile, path.join(addonPath, 'install.rdf'));
        173 });
        174 }
        175
        176 return manifest.then(function(content) {
        177 return checkedCall(xml.parseString, content);
        178 });
        179}
        180
        181
        182// PUBLIC API
        183
        184
        185exports.install = install;
        \ No newline at end of file diff --git a/docs/source/firefox/index.js.src.html b/docs/source/firefox/index.js.src.html new file mode 100644 index 0000000..b5ea228 --- /dev/null +++ b/docs/source/firefox/index.js.src.html @@ -0,0 +1 @@ +index.js

        firefox/index.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines the {@linkplain Driver WebDriver} client for Firefox.
        20 * Each FirefoxDriver instance will be created with an anonymous profile,
        21 * ensuring browser historys do not share session data (cookies, history, cache,
        22 * offline storage, etc.)
        23 *
        24 * __Customizing the Firefox Profile__
        25 *
        26 * The {@link Profile} class may be used to configure the browser profile used
        27 * with WebDriver, with functions to install additional
        28 * {@linkplain Profile#addExtension extensions}, configure browser
        29 * {@linkplain Profile#setPreference preferences}, and more. For example, you
        30 * may wish to include Firebug:
        31 *
        32 * var firefox = require('selenium-webdriver/firefox');
        33 *
        34 * var profile = new firefox.Profile();
        35 * profile.addExtension('/path/to/firebug.xpi');
        36 * profile.setPreference('extensions.firebug.showChromeErrors', true);
        37 *
        38 * var options = new firefox.Options().setProfile(profile);
        39 * var driver = new firefox.Driver(options);
        40 *
        41 * The {@link Profile} class may also be used to configure WebDriver based on a
        42 * pre-existing browser profile:
        43 *
        44 * var profile = new firefox.Profile(
        45 * '/usr/local/home/bob/.mozilla/firefox/3fgog75h.testing');
        46 * var options = new firefox.Options().setProfile(profile);
        47 * var driver = new firefox.Driver(options);
        48 *
        49 * The FirefoxDriver will _never_ modify a pre-existing profile; instead it will
        50 * create a copy for it to modify. By extension, there are certain browser
        51 * preferences that are required for WebDriver to function properly and they
        52 * will always be overwritten.
        53 *
        54 * __Using a Custom Firefox Binary__
        55 *
        56 * On Windows and OSX, the FirefoxDriver will search for Firefox in its
        57 * default installation location:
        58 *
        59 * * Windows: C:\Program Files and C:\Program Files (x86).
        60 * * Mac OS X: /Applications/Firefox.app
        61 *
        62 * For Linux, Firefox will be located on the PATH: `$(where firefox)`.
        63 *
        64 * You can configure WebDriver to start use a custom Firefox installation with
        65 * the {@link Binary} class:
        66 *
        67 * var firefox = require('selenium-webdriver/firefox');
        68 * var binary = new firefox.Binary('/my/firefox/install/dir/firefox-bin');
        69 * var options = new firefox.Options().setBinary(binary);
        70 * var driver = new firefox.Driver(options);
        71 *
        72 * __Remote Testing__
        73 *
        74 * You may customize the Firefox binary and profile when running against a
        75 * remote Selenium server. Your custom profile will be packaged as a zip and
        76 * transfered to the remote host for use. The profile will be transferred
        77 * _once for each new session_. The performance impact should be minimal if
        78 * you've only configured a few extra browser preferences. If you have a large
        79 * profile with several extensions, you should consider installing it on the
        80 * remote host and defining its path via the {@link Options} class. Custom
        81 * binaries are never copied to remote machines and must be referenced by
        82 * installation path.
        83 *
        84 * var options = new firefox.Options()
        85 * .setProfile('/profile/path/on/remote/host')
        86 * .setBinary('/install/dir/on/remote/host/firefox-bin');
        87 *
        88 * var driver = new (require('selenium-webdriver')).Builder()
        89 * .forBrowser('firefox')
        90 * .usingServer('http://127.0.0.1:4444/wd/hub')
        91 * .setFirefoxOptions(options)
        92 * .build();
        93 */
        94
        95'use strict';
        96
        97var url = require('url'),
        98 util = require('util');
        99
        100var Binary = require('./binary').Binary,
        101 Profile = require('./profile').Profile,
        102 decodeProfile = require('./profile').decode,
        103 webdriver = require('..'),
        104 executors = require('../executors'),
        105 httpUtil = require('../http/util'),
        106 io = require('../io'),
        107 net = require('../net'),
        108 portprober = require('../net/portprober');
        109
        110
        111/**
        112 * Configuration options for the FirefoxDriver.
        113 * @constructor
        114 */
        115var Options = function() {
        116 /** @private {Profile} */
        117 this.profile_ = null;
        118
        119 /** @private {Binary} */
        120 this.binary_ = null;
        121
        122 /** @private {webdriver.logging.Preferences} */
        123 this.logPrefs_ = null;
        124
        125 /** @private {webdriver.ProxyConfig} */
        126 this.proxy_ = null;
        127};
        128
        129
        130/**
        131 * Sets the profile to use. The profile may be specified as a
        132 * {@link Profile} object or as the path to an existing Firefox profile to use
        133 * as a template.
        134 *
        135 * @param {(string|!Profile)} profile The profile to use.
        136 * @return {!Options} A self reference.
        137 */
        138Options.prototype.setProfile = function(profile) {
        139 if (typeof profile === 'string') {
        140 profile = new Profile(profile);
        141 }
        142 this.profile_ = profile;
        143 return this;
        144};
        145
        146
        147/**
        148 * Sets the binary to use. The binary may be specified as the path to a Firefox
        149 * executable, or as a {@link Binary} object.
        150 *
        151 * @param {(string|!Binary)} binary The binary to use.
        152 * @return {!Options} A self reference.
        153 */
        154Options.prototype.setBinary = function(binary) {
        155 if (typeof binary === 'string') {
        156 binary = new Binary(binary);
        157 }
        158 this.binary_ = binary;
        159 return this;
        160};
        161
        162
        163/**
        164 * Sets the logging preferences for the new session.
        165 * @param {webdriver.logging.Preferences} prefs The logging preferences.
        166 * @return {!Options} A self reference.
        167 */
        168Options.prototype.setLoggingPreferences = function(prefs) {
        169 this.logPrefs_ = prefs;
        170 return this;
        171};
        172
        173
        174/**
        175 * Sets the proxy to use.
        176 *
        177 * @param {webdriver.ProxyConfig} proxy The proxy configuration to use.
        178 * @return {!Options} A self reference.
        179 */
        180Options.prototype.setProxy = function(proxy) {
        181 this.proxy_ = proxy;
        182 return this;
        183};
        184
        185
        186/**
        187 * Converts these options to a {@link webdriver.Capabilities} instance.
        188 *
        189 * @return {!webdriver.Capabilities} A new capabilities object.
        190 */
        191Options.prototype.toCapabilities = function(opt_remote) {
        192 var caps = webdriver.Capabilities.firefox();
        193 if (this.logPrefs_) {
        194 caps.set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_);
        195 }
        196 if (this.proxy_) {
        197 caps.set(webdriver.Capability.PROXY, this.proxy_);
        198 }
        199 if (this.binary_) {
        200 caps.set('firefox_binary', this.binary_);
        201 }
        202 if (this.profile_) {
        203 caps.set('firefox_profile', this.profile_);
        204 }
        205 return caps;
        206};
        207
        208
        209/**
        210 * A WebDriver client for Firefox.
        211 *
        212 * @param {(Options|webdriver.Capabilities|Object)=} opt_config The
        213 * configuration options for this driver, specified as either an
        214 * {@link Options} or {@link webdriver.Capabilities}, or as a raw hash
        215 * object.
        216 * @param {webdriver.promise.ControlFlow=} opt_flow The flow to
        217 * schedule commands through. Defaults to the active flow object.
        218 * @constructor
        219 * @extends {webdriver.WebDriver}
        220 */
        221var Driver = function(opt_config, opt_flow) {
        222 var caps;
        223 if (opt_config instanceof Options) {
        224 caps = opt_config.toCapabilities();
        225 } else {
        226 caps = new webdriver.Capabilities(opt_config);
        227 }
        228
        229 var binary = caps.get('firefox_binary') || new Binary();
        230 if (typeof binary === 'string') {
        231 binary = new Binary(binary);
        232 }
        233
        234 var profile = caps.get('firefox_profile') || new Profile();
        235
        236 caps.set('firefox_binary', null);
        237 caps.set('firefox_profile', null);
        238
        239 /** @private {?string} */
        240 this.profilePath_ = null;
        241
        242 var self = this;
        243 var freePort = portprober.findFreePort();
        244
        245 /** @private */
        246 this.command_ = freePort.then(function(port) {
        247 if (typeof profile === 'string') {
        248 return decodeProfile(profile).then(function(dir) {
        249 var profile = new Profile(dir);
        250 profile.setPreference('webdriver_firefox_port', port);
        251 return profile.writeToDisk();
        252 });
        253 } else {
        254 profile.setPreference('webdriver_firefox_port', port);
        255 return profile.writeToDisk();
        256 }
        257 }).then(function(profileDir) {
        258 self.profilePath_ = profileDir;
        259 return binary.launch(profileDir);
        260 });
        261
        262 var serverUrl = this.command_
        263 .then(function() { return freePort; })
        264 .then(function(port) {
        265 var serverUrl = url.format({
        266 protocol: 'http',
        267 hostname: net.getLoopbackAddress(),
        268 port: port,
        269 pathname: '/hub'
        270 });
        271
        272 return httpUtil.waitForServer(serverUrl, 45 * 1000).then(function() {
        273 return serverUrl;
        274 });
        275 });
        276
        277 var executor = executors.createExecutor(serverUrl);
        278 var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow);
        279
        280 webdriver.WebDriver.call(this, driver.getSession(), executor, opt_flow);
        281};
        282util.inherits(Driver, webdriver.WebDriver);
        283
        284
        285/**
        286 * This function is a no-op as file detectors are not supported by this
        287 * implementation.
        288 * @override
        289 */
        290Driver.prototype.setFileDetector = function() {
        291};
        292
        293
        294/** @override */
        295Driver.prototype.quit = function() {
        296 return this.call(function() {
        297 var self = this;
        298 return Driver.super_.prototype.quit.call(this)
        299 .thenFinally(function() {
        300 return self.command_.then(function(command) {
        301 command.kill();
        302 return command.result();
        303 });
        304 })
        305 .thenFinally(function() {
        306 if (self.profilePath_) {
        307 return io.rmDir(self.profilePath_);
        308 }
        309 });
        310 }, this);
        311};
        312
        313
        314// PUBLIC API
        315
        316
        317exports.Binary = Binary;
        318exports.Driver = Driver;
        319exports.Options = Options;
        320exports.Profile = Profile;
        \ No newline at end of file diff --git a/docs/source/firefox/profile.js.src.html b/docs/source/firefox/profile.js.src.html new file mode 100644 index 0000000..79fcdf6 --- /dev/null +++ b/docs/source/firefox/profile.js.src.html @@ -0,0 +1 @@ +profile.js

        firefox/profile.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Profile management module. This module is considered internal;
        20 * users should use {@link selenium-webdriver/firefox}.
        21 */
        22
        23'use strict';
        24
        25var AdmZip = require('adm-zip'),
        26 AdmConstants = require('adm-zip/util/constants'),
        27 fs = require('fs'),
        28 path = require('path'),
        29 util = require('util'),
        30 vm = require('vm');
        31
        32var Serializable = require('..').Serializable,
        33 promise = require('..').promise,
        34 _base = require('../_base'),
        35 io = require('../io'),
        36 extension = require('./extension');
        37
        38
        39/** @const */
        40var WEBDRIVER_PREFERENCES_PATH = _base.isDevMode()
        41 ? path.join(__dirname, '../../../firefox-driver/webdriver.json')
        42 : path.join(__dirname, '../lib/firefox/webdriver.json');
        43
        44/** @const */
        45var WEBDRIVER_EXTENSION_PATH = _base.isDevMode()
        46 ? path.join(__dirname,
        47 '../../../../build/javascript/firefox-driver/webdriver.xpi')
        48 : path.join(__dirname, '../lib/firefox/webdriver.xpi');
        49
        50/** @const */
        51var WEBDRIVER_EXTENSION_NAME = 'fxdriver@googlecode.com';
        52
        53
        54
        55/** @type {Object} */
        56var defaultPreferences = null;
        57
        58/**
        59 * Synchronously loads the default preferences used for the FirefoxDriver.
        60 * @return {!Object} The default preferences JSON object.
        61 */
        62function getDefaultPreferences() {
        63 if (!defaultPreferences) {
        64 var contents = fs.readFileSync(WEBDRIVER_PREFERENCES_PATH, 'utf8');
        65 defaultPreferences = JSON.parse(contents);
        66 }
        67 return defaultPreferences;
        68}
        69
        70
        71/**
        72 * Parses a user.js file in a Firefox profile directory.
        73 * @param {string} f Path to the file to parse.
        74 * @return {!promise.Promise.<!Object>} A promise for the parsed preferences as
        75 * a JSON object. If the file does not exist, an empty object will be
        76 * returned.
        77 */
        78function loadUserPrefs(f) {
        79 var done = promise.defer();
        80 fs.readFile(f, function(err, contents) {
        81 if (err && err.code === 'ENOENT') {
        82 done.fulfill({});
        83 return;
        84 }
        85
        86 if (err) {
        87 done.reject(err);
        88 return;
        89 }
        90
        91 var prefs = {};
        92 var context = vm.createContext({
        93 'user_pref': function(key, value) {
        94 prefs[key] = value;
        95 }
        96 });
        97
        98 vm.runInContext(contents, context, f);
        99 done.fulfill(prefs);
        100 });
        101 return done.promise;
        102}
        103
        104
        105/**
        106 * Copies the properties of one object into another.
        107 * @param {!Object} a The destination object.
        108 * @param {!Object} b The source object to apply as a mixin.
        109 */
        110function mixin(a, b) {
        111 Object.keys(b).forEach(function(key) {
        112 a[key] = b[key];
        113 });
        114}
        115
        116
        117/**
        118 * @param {!Object} defaults The default preferences to write. Will be
        119 * overridden by user.js preferences in the template directory and the
        120 * frozen preferences required by WebDriver.
        121 * @param {string} dir Path to the directory write the file to.
        122 * @return {!promise.Promise.<string>} A promise for the profile directory,
        123 * to be fulfilled when user preferences have been written.
        124 */
        125function writeUserPrefs(prefs, dir) {
        126 var userPrefs = path.join(dir, 'user.js');
        127 return loadUserPrefs(userPrefs).then(function(overrides) {
        128 mixin(prefs, overrides);
        129 mixin(prefs, getDefaultPreferences()['frozen']);
        130
        131 var contents = Object.keys(prefs).map(function(key) {
        132 return 'user_pref(' + JSON.stringify(key) + ', ' +
        133 JSON.stringify(prefs[key]) + ');';
        134 }).join('\n');
        135
        136 var done = promise.defer();
        137 fs.writeFile(userPrefs, contents, function(err) {
        138 err && done.reject(err) || done.fulfill(dir);
        139 });
        140 return done.promise;
        141 });
        142};
        143
        144
        145/**
        146 * Installs a group of extensions in the given profile directory. If the
        147 * WebDriver extension is not included in this set, the default version
        148 * bundled with this package will be installed.
        149 * @param {!Array.<string>} extensions The extensions to install, as a
        150 * path to an unpacked extension directory or a path to a xpi file.
        151 * @param {string} dir The profile directory to install to.
        152 * @param {boolean=} opt_excludeWebDriverExt Whether to skip installation of
        153 * the default WebDriver extension.
        154 * @return {!promise.Promise.<string>} A promise for the main profile directory
        155 * once all extensions have been installed.
        156 */
        157function installExtensions(extensions, dir, opt_excludeWebDriverExt) {
        158 var hasWebDriver = !!opt_excludeWebDriverExt;
        159 var next = 0;
        160 var extensionDir = path.join(dir, 'extensions');
        161 var done = promise.defer();
        162
        163 return io.exists(extensionDir).then(function(exists) {
        164 if (!exists) {
        165 return promise.checkedNodeCall(fs.mkdir, extensionDir);
        166 }
        167 }).then(function() {
        168 installNext();
        169 return done.promise;
        170 });
        171
        172 function installNext() {
        173 if (!done.isPending()) {
        174 return;
        175 }
        176
        177 if (next >= extensions.length) {
        178 if (hasWebDriver) {
        179 done.fulfill(dir);
        180 } else {
        181 install(WEBDRIVER_EXTENSION_PATH);
        182 }
        183 } else {
        184 install(extensions[next++]);
        185 }
        186 }
        187
        188 function install(ext) {
        189 extension.install(ext, extensionDir).then(function(id) {
        190 hasWebDriver = hasWebDriver || (id === WEBDRIVER_EXTENSION_NAME);
        191 installNext();
        192 }, done.reject);
        193 }
        194}
        195
        196
        197/**
        198 * Decodes a base64 encoded profile.
        199 * @param {string} data The base64 encoded string.
        200 * @return {!promise.Promise.<string>} A promise for the path to the decoded
        201 * profile directory.
        202 */
        203function decode(data) {
        204 return io.tmpFile().then(function(file) {
        205 var buf = new Buffer(data, 'base64');
        206 return promise.checkedNodeCall(fs.writeFile, file, buf).then(function() {
        207 return io.tmpDir();
        208 }).then(function(dir) {
        209 var zip = new AdmZip(file);
        210 zip.extractAllTo(dir); // Sync only? Why?? :-(
        211 return dir;
        212 });
        213 });
        214}
        215
        216
        217
        218/**
        219 * Models a Firefox proifle directory for use with the FirefoxDriver. The
        220 * {@code Proifle} directory uses an in-memory model until {@link #writeToDisk}
        221 * is called.
        222 * @param {string=} opt_dir Path to an existing Firefox profile directory to
        223 * use a template for this profile. If not specified, a blank profile will
        224 * be used.
        225 * @constructor
        226 * @extends {Serializable.<string>}
        227 */
        228var Profile = function(opt_dir) {
        229 Serializable.call(this);
        230
        231 /** @private {!Object} */
        232 this.preferences_ = {};
        233
        234 mixin(this.preferences_, getDefaultPreferences()['mutable']);
        235 mixin(this.preferences_, getDefaultPreferences()['frozen']);
        236
        237 /** @private {boolean} */
        238 this.nativeEventsEnabled_ = true;
        239
        240 /** @private {(string|undefined)} */
        241 this.template_ = opt_dir;
        242
        243 /** @private {number} */
        244 this.port_ = 0;
        245
        246 /** @private {!Array.<string>} */
        247 this.extensions_ = [];
        248};
        249util.inherits(Profile, Serializable);
        250
        251
        252/**
        253 * Registers an extension to be included with this profile.
        254 * @param {string} extension Path to the extension to include, as either an
        255 * unpacked extension directory or the path to a xpi file.
        256 */
        257Profile.prototype.addExtension = function(extension) {
        258 this.extensions_.push(extension);
        259};
        260
        261
        262/**
        263 * Sets a desired preference for this profile.
        264 * @param {string} key The preference key.
        265 * @param {(string|number|boolean)} value The preference value.
        266 * @throws {Error} If attempting to set a frozen preference.
        267 */
        268Profile.prototype.setPreference = function(key, value) {
        269 var frozen = getDefaultPreferences()['frozen'];
        270 if (frozen.hasOwnProperty(key) && frozen[key] !== value) {
        271 throw Error('You may not set ' + key + '=' + JSON.stringify(value)
        272 + '; value is frozen for proper WebDriver functionality ('
        273 + key + '=' + JSON.stringify(frozen[key]) + ')');
        274 }
        275 this.preferences_[key] = value;
        276};
        277
        278
        279/**
        280 * Returns the currently configured value of a profile preference. This does
        281 * not include any defaults defined in the profile's template directory user.js
        282 * file (if a template were specified on construction).
        283 * @param {string} key The desired preference.
        284 * @return {(string|number|boolean|undefined)} The current value of the
        285 * requested preference.
        286 */
        287Profile.prototype.getPreference = function(key) {
        288 return this.preferences_[key];
        289};
        290
        291
        292/**
        293 * @return {number} The port this profile is currently configured to use, or
        294 * 0 if the port will be selected at random when the profile is written
        295 * to disk.
        296 */
        297Profile.prototype.getPort = function() {
        298 return this.port_;
        299};
        300
        301
        302/**
        303 * Sets the port to use for the WebDriver extension loaded by this profile.
        304 * @param {number} port The desired port, or 0 to use any free port.
        305 */
        306Profile.prototype.setPort = function(port) {
        307 this.port_ = port;
        308};
        309
        310
        311/**
        312 * @return {boolean} Whether the FirefoxDriver is configured to automatically
        313 * accept untrusted SSL certificates.
        314 */
        315Profile.prototype.acceptUntrustedCerts = function() {
        316 return !!this.preferences_['webdriver_accept_untrusted_certs'];
        317};
        318
        319
        320/**
        321 * Sets whether the FirefoxDriver should automatically accept untrusted SSL
        322 * certificates.
        323 * @param {boolean} value .
        324 */
        325Profile.prototype.setAcceptUntrustedCerts = function(value) {
        326 this.preferences_['webdriver_accept_untrusted_certs'] = !!value;
        327};
        328
        329
        330/**
        331 * Sets whether to assume untrusted certificates come from untrusted issuers.
        332 * @param {boolean} value .
        333 */
        334Profile.prototype.setAssumeUntrustedCertIssuer = function(value) {
        335 this.preferences_['webdriver_assume_untrusted_issuer'] = !!value;
        336};
        337
        338
        339/**
        340 * @return {boolean} Whether to assume untrusted certs come from untrusted
        341 * issuers.
        342 */
        343Profile.prototype.assumeUntrustedCertIssuer = function() {
        344 return !!this.preferences_['webdriver_assume_untrusted_issuer'];
        345};
        346
        347
        348/**
        349 * Sets whether to use native events with this profile.
        350 * @param {boolean} enabled .
        351 */
        352Profile.prototype.setNativeEventsEnabled = function(enabled) {
        353 this.nativeEventsEnabled_ = enabled;
        354};
        355
        356
        357/**
        358 * Returns whether native events are enabled in this profile.
        359 * @return {boolean} .
        360 */
        361Profile.prototype.nativeEventsEnabled = function() {
        362 return this.nativeEventsEnabled_;
        363};
        364
        365
        366/**
        367 * Writes this profile to disk.
        368 * @param {boolean=} opt_excludeWebDriverExt Whether to exclude the WebDriver
        369 * extension from the generated profile. Used to reduce the size of an
        370 * {@link #encode() encoded profile} since the server will always install
        371 * the extension itself.
        372 * @return {!promise.Promise.<string>} A promise for the path to the new
        373 * profile directory.
        374 */
        375Profile.prototype.writeToDisk = function(opt_excludeWebDriverExt) {
        376 var profileDir = io.tmpDir();
        377 if (this.template_) {
        378 profileDir = profileDir.then(function(dir) {
        379 return io.copyDir(
        380 this.template_, dir, /(parent\.lock|lock|\.parentlock)/);
        381 }.bind(this));
        382 }
        383
        384 // Freeze preferences for async operations.
        385 var prefs = {};
        386 mixin(prefs, this.preferences_);
        387
        388 // Freeze extensions for async operations.
        389 var extensions = this.extensions_.concat();
        390
        391 return profileDir.then(function(dir) {
        392 return writeUserPrefs(prefs, dir);
        393 }).then(function(dir) {
        394 return installExtensions(extensions, dir, !!opt_excludeWebDriverExt);
        395 });
        396};
        397
        398
        399/**
        400 * Encodes this profile as a zipped, base64 encoded directory.
        401 * @return {!promise.Promise.<string>} A promise for the encoded profile.
        402 */
        403Profile.prototype.encode = function() {
        404 return this.writeToDisk(true).then(function(dir) {
        405 var zip = new AdmZip();
        406 zip.addLocalFolder(dir, '');
        407 zip.getEntries()[0].header.method = AdmConstants.STORED;
        408 return io.tmpFile().then(function(file) {
        409 zip.writeZip(file); // Sync! Why oh why :-(
        410 return promise.checkedNodeCall(fs.readFile, file);
        411 });
        412 }).then(function(data) {
        413 return new Buffer(data).toString('base64');
        414 });
        415};
        416
        417
        418/**
        419 * Encodes this profile as a zipped, base64 encoded directory.
        420 * @return {!promise.Promise.<string>} A promise for the encoded profile.
        421 * @override
        422 */
        423Profile.prototype.serialize = function() {
        424 return this.encode();
        425};
        426
        427
        428// PUBLIC API
        429
        430
        431exports.Profile = Profile;
        432exports.decode = decode;
        433exports.loadUserPrefs = loadUserPrefs;
        \ No newline at end of file diff --git a/docs/source/http/index.js.src.html b/docs/source/http/index.js.src.html index 44e2cb2..f99eaa9 100644 --- a/docs/source/http/index.js.src.html +++ b/docs/source/http/index.js.src.html @@ -1 +1 @@ -index.js

        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
        20var http = require('http'),
        21 url = require('url');
        22
        23var 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 */
        34var 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 */
        53HttpClient.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 */
        86var sendRequest = function(options, callback, opt_data) {
        87 var request = http.request(options, function(response) {
        88 if (response.statusCode == 302 || response.statusCode == 303) {
        89 var location = url.parse(response.headers['location']);
        90
        91 if (!location.hostname) {
        92 location.hostname = options.host;
        93 location.port = options.port;
        94 }
        95
        96 request.abort();
        97 sendRequest({
        98 method: 'GET',
        99 host: location.hostname,
        100 path: location.pathname + (location.search || ''),
        101 port: location.port,
        102 headers: {
        103 'Accept': 'application/json; charset=utf-8'
        104 }
        105 }, callback);
        106 return;
        107 }
        108
        109 var body = [];
        110 response.on('data', body.push.bind(body));
        111 response.on('end', function() {
        112 var resp = new HttpResponse(response.statusCode,
        113 response.headers, body.join('').replace(/\0/g, ''));
        114 callback(null, resp);
        115 });
        116 });
        117
        118 request.on('error', function(e) {
        119 if (e.code === 'ECONNRESET') {
        120 setTimeout(function() {
        121 sendRequest(options, callback, opt_data);
        122 }, 15);
        123 } else {
        124 var message = e.message;
        125 if (e.code) {
        126 message = e.code + ' ' + message;
        127 }
        128 callback(new Error(message));
        129 }
        130 });
        131
        132 if (opt_data) {
        133 request.write(opt_data);
        134 }
        135
        136 request.end();
        137};
        138
        139
        140// PUBLIC API
        141
        142/** @type {webdriver.http.Executor.} */
        143exports.Executor = base.require('webdriver.http.Executor');
        144
        145/** @type {webdriver.http.Request.} */
        146exports.Request = base.require('webdriver.http.Request');
        147
        148/** @type {webdriver.http.Response.} */
        149exports.Response = base.require('webdriver.http.Response');
        150
        151exports.HttpClient = HttpClient;
        \ No newline at end of file +index.js

        http/index.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines the {@code webdriver.http.Client} for use with
        20 * NodeJS.
        21 */
        22
        23var http = require('http'),
        24 url = require('url');
        25
        26var base = require('../_base'),
        27 HttpResponse = base.require('webdriver.http.Response');
        28
        29
        30/**
        31 * A {@link webdriver.http.Client} implementation using Node's built-in http
        32 * module.
        33 * @param {string} serverUrl URL for the WebDriver server to send commands to.
        34 * @param {http.Agent=} opt_agent The agent to use for each request.
        35 * Defaults to {@code http.globalAgent}.
        36 * @param {string=} opt_proxy The proxy to use for the connection to the server.
        37 * Default is to use no proxy.
        38 * @constructor
        39 * @implements {webdriver.http.Client}
        40 */
        41var HttpClient = function(serverUrl, opt_agent, opt_proxy) {
        42 var parsedUrl = url.parse(serverUrl);
        43 if (!parsedUrl.hostname) {
        44 throw new Error('Invalid server URL: ' + serverUrl);
        45 }
        46
        47 /** @private {http.Agent} */
        48 this.agent_ = opt_agent;
        49
        50 /** @private {string} */
        51 this.proxy_ = opt_proxy;
        52
        53 /**
        54 * Base options for each request.
        55 * @private {!Object}
        56 */
        57 this.options_ = {
        58 auth: parsedUrl.auth,
        59 host: parsedUrl.hostname,
        60 path: parsedUrl.pathname,
        61 port: parsedUrl.port
        62 };
        63};
        64
        65
        66/** @override */
        67HttpClient.prototype.send = function(httpRequest, callback) {
        68 var data;
        69 httpRequest.headers['Content-Length'] = 0;
        70 if (httpRequest.method == 'POST' || httpRequest.method == 'PUT') {
        71 data = JSON.stringify(httpRequest.data);
        72 httpRequest.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
        73 httpRequest.headers['Content-Type'] = 'application/json;charset=UTF-8';
        74 }
        75
        76 var path = this.options_.path;
        77 if (path[path.length - 1] === '/' && httpRequest.path[0] === '/') {
        78 path += httpRequest.path.substring(1);
        79 } else {
        80 path += httpRequest.path;
        81 }
        82
        83 var options = {
        84 method: httpRequest.method,
        85 auth: this.options_.auth,
        86 host: this.options_.host,
        87 port: this.options_.port,
        88 path: path,
        89 headers: httpRequest.headers
        90 };
        91
        92 if (this.agent_) {
        93 options.agent = this.agent_;
        94 }
        95
        96 sendRequest(options, callback, data, this.proxy_);
        97};
        98
        99
        100/**
        101 * Sends a single HTTP request.
        102 * @param {!Object} options The request options.
        103 * @param {function(Error, !webdriver.http.Response=)} callback The function to
        104 * invoke with the server's response.
        105 * @param {string=} opt_data The data to send with the request.
        106 * @param {string=} opt_proxy The proxy server to use for the request.
        107 */
        108var sendRequest = function(options, callback, opt_data, opt_proxy) {
        109 var host = options.host;
        110 var port = options.port;
        111
        112 if (opt_proxy) {
        113 var proxy = url.parse(opt_proxy);
        114
        115 options.headers['Host'] = options.host;
        116 options.host = proxy.hostname;
        117 options.port = proxy.port;
        118
        119 if (proxy.auth) {
        120 options.headers['Proxy-Authorization'] =
        121 'Basic ' + new Buffer(proxy.auth).toString('base64');
        122 }
        123 }
        124
        125 var request = http.request(options, function(response) {
        126 if (response.statusCode == 302 || response.statusCode == 303) {
        127 try {
        128 var location = url.parse(response.headers['location']);
        129 } catch (ex) {
        130 callback(Error(
        131 'Failed to parse "Location" header for server redirect: ' +
        132 ex.message + '\nResponse was: \n' +
        133 new HttpResponse(response.statusCode, response.headers, '')));
        134 return;
        135 }
        136
        137 if (!location.hostname) {
        138 location.hostname = host;
        139 location.port = port;
        140 }
        141
        142 request.abort();
        143 sendRequest({
        144 method: 'GET',
        145 host: location.hostname,
        146 path: location.pathname + (location.search || ''),
        147 port: location.port,
        148 headers: {
        149 'Accept': 'application/json; charset=utf-8'
        150 }
        151 }, callback, undefined, opt_proxy);
        152 return;
        153 }
        154
        155 var body = [];
        156 response.on('data', body.push.bind(body));
        157 response.on('end', function() {
        158 var resp = new HttpResponse(response.statusCode,
        159 response.headers, body.join('').replace(/\0/g, ''));
        160 callback(null, resp);
        161 });
        162 });
        163
        164 request.on('error', function(e) {
        165 if (e.code === 'ECONNRESET') {
        166 setTimeout(function() {
        167 sendRequest(options, callback, opt_data, opt_proxy);
        168 }, 15);
        169 } else {
        170 var message = e.message;
        171 if (e.code) {
        172 message = e.code + ' ' + message;
        173 }
        174 callback(new Error(message));
        175 }
        176 });
        177
        178 if (opt_data) {
        179 request.write(opt_data);
        180 }
        181
        182 request.end();
        183};
        184
        185
        186// PUBLIC API
        187
        188/** @type {webdriver.http.Executor.} */
        189exports.Executor = base.require('webdriver.http.Executor');
        190
        191/** @type {webdriver.http.Request.} */
        192exports.Request = base.require('webdriver.http.Request');
        193
        194/** @type {webdriver.http.Response.} */
        195exports.Response = base.require('webdriver.http.Response');
        196
        197exports.HttpClient = HttpClient;
        \ No newline at end of file diff --git a/docs/source/http/util.js.src.html b/docs/source/http/util.js.src.html index f4b4915..e0a159c 100644 --- a/docs/source/http/util.js.src.html +++ b/docs/source/http/util.js.src.html @@ -1 +1 @@ -util.js

        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
        20var 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 */
        37function 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 */
        62exports.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 */
        74exports.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 */
        107exports.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};
        \ No newline at end of file +util.js

        http/util.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Various HTTP utilities.
        20 */
        21
        22var base = require('../_base'),
        23 HttpClient = require('./index').HttpClient,
        24 checkResponse = base.require('bot.response').checkResponse,
        25 Executor = base.require('webdriver.http.Executor'),
        26 HttpRequest = base.require('webdriver.http.Request'),
        27 Command = base.require('webdriver.Command'),
        28 CommandName = base.require('webdriver.CommandName'),
        29 promise = base.require('webdriver.promise');
        30
        31
        32
        33/**
        34 * Queries a WebDriver server for its current status.
        35 * @param {string} url Base URL of the server to query.
        36 * @param {function(Error, *=)} callback The function to call with the
        37 * response.
        38 */
        39function getStatus(url, callback) {
        40 var client = new HttpClient(url);
        41 var executor = new Executor(client);
        42 var command = new Command(CommandName.GET_SERVER_STATUS);
        43 executor.execute(command, function(err, responseObj) {
        44 if (err) return callback(err);
        45 try {
        46 checkResponse(responseObj);
        47 } catch (ex) {
        48 return callback(ex);
        49 }
        50 callback(null, responseObj['value']);
        51 });
        52}
        53
        54
        55// PUBLIC API
        56
        57
        58/**
        59 * Queries a WebDriver server for its current status.
        60 * @param {string} url Base URL of the server to query.
        61 * @return {!webdriver.promise.Promise.<!Object>} A promise that resolves with
        62 * a hash of the server status.
        63 */
        64exports.getStatus = function(url) {
        65 return promise.checkedNodeCall(getStatus.bind(null, url));
        66};
        67
        68
        69/**
        70 * Waits for a WebDriver server to be healthy and accepting requests.
        71 * @param {string} url Base URL of the server to query.
        72 * @param {number} timeout How long to wait for the server.
        73 * @return {!webdriver.promise.Promise} A promise that will resolve when the
        74 * server is ready.
        75 */
        76exports.waitForServer = function(url, timeout) {
        77 var ready = promise.defer(),
        78 start = Date.now(),
        79 checkServerStatus = getStatus.bind(null, url, onResponse);
        80 checkServerStatus();
        81 return ready.promise;
        82
        83 function onResponse(err) {
        84 if (!ready.isPending()) return;
        85 if (!err) return ready.fulfill();
        86
        87 if (Date.now() - start > timeout) {
        88 ready.reject(
        89 Error('Timed out waiting for the WebDriver server at ' + url));
        90 } else {
        91 setTimeout(function() {
        92 if (ready.isPending()) {
        93 checkServerStatus();
        94 }
        95 }, 50);
        96 }
        97 }
        98};
        99
        100
        101/**
        102 * Polls a URL with GET requests until it returns a 2xx response or the
        103 * timeout expires.
        104 * @param {string} url The URL to poll.
        105 * @param {number} timeout How long to wait, in milliseconds.
        106 * @return {!webdriver.promise.Promise} A promise that will resolve when the
        107 * URL responds with 2xx.
        108 */
        109exports.waitForUrl = function(url, timeout) {
        110 var client = new HttpClient(url),
        111 request = new HttpRequest('GET', ''),
        112 testUrl = client.send.bind(client, request, onResponse),
        113 ready = promise.defer(),
        114 start = Date.now();
        115 testUrl();
        116 return ready.promise;
        117
        118 function onResponse(err, response) {
        119 if (!ready.isPending()) return;
        120 if (!err && response.status > 199 && response.status < 300) {
        121 return ready.fulfill();
        122 }
        123
        124 if (Date.now() - start > timeout) {
        125 ready.reject(Error(
        126 'Timed out waiting for the URL to return 2xx: ' + url));
        127 } else {
        128 setTimeout(function() {
        129 if (ready.isPending()) {
        130 testUrl();
        131 }
        132 }, 50);
        133 }
        134 }
        135};
        \ No newline at end of file diff --git a/docs/source/ie.js.src.html b/docs/source/ie.js.src.html new file mode 100644 index 0000000..9591bb8 --- /dev/null +++ b/docs/source/ie.js.src.html @@ -0,0 +1 @@ +ie.js

        ie.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines a {@linkplain Driver WebDriver} client for Microsoft's
        20 * Internet Explorer. Before using the IEDriver, you must download the latest
        21 * [IEDriverServer](http://selenium-release.storage.googleapis.com/index.html)
        22 * and place it on your
        23 * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). You must also apply
        24 * the system configuration outlined on the Selenium project
        25 * [wiki](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
        26 */
        27
        28'use strict';
        29
        30var fs = require('fs'),
        31 util = require('util');
        32
        33var webdriver = require('./index'),
        34 executors = require('./executors'),
        35 io = require('./io'),
        36 portprober = require('./net/portprober'),
        37 remote = require('./remote');
        38
        39
        40/**
        41 * @const
        42 * @final
        43 */
        44var IEDRIVER_EXE = 'IEDriverServer.exe';
        45
        46
        47
        48/**
        49 * IEDriverServer logging levels.
        50 * @enum {string}
        51 */
        52var Level = {
        53 FATAL: 'FATAL',
        54 ERROR: 'ERROR',
        55 WARN: 'WARN',
        56 INFO: 'INFO',
        57 DEBUG: 'DEBUG',
        58 TRACE: 'TRACE'
        59};
        60
        61
        62
        63/**
        64 * Option keys:
        65 * https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#ie-specific
        66 * @enum {string}
        67 */
        68var Key = {
        69 IGNORE_PROTECTED_MODE_SETTINGS: 'ignoreProtectedModeSettings',
        70 IGNORE_ZOOM_SETTING: 'ignoreZoomSetting',
        71 INITIAL_BROWSER_URL: 'initialBrowserUrl',
        72 ENABLE_PERSISTENT_HOVER: 'enablePersistentHover',
        73 ENABLE_ELEMENT_CACHE_CLEANUP: 'enableElementCacheCleanup',
        74 REQUIRE_WINDOW_FOCUS: 'requireWindowFocus',
        75 BROWSER_ATTACH_TIMEOUT: 'browserAttachTimeout',
        76 FORCE_CREATE_PROCESS: 'ie.forceCreateProcessApi',
        77 BROWSER_COMMAND_LINE_SWITCHES: 'ie.browserCommandLineSwitches',
        78 USE_PER_PROCESS_PROXY: 'ie.usePerProcessProxy',
        79 ENSURE_CLEAN_SESSION: 'ie.ensureCleanSession',
        80 LOG_FILE: 'logFile',
        81 LOG_LEVEL: 'logLevel',
        82 HOST: 'host',
        83 EXTRACT_PATH: 'extractPath',
        84 SILENT: 'silent'
        85};
        86
        87
        88/**
        89 * Class for managing IEDriver specific options.
        90 * @constructor
        91 */
        92var Options = function() {
        93 /** @private {!Object<(boolean|number|string)>} */
        94 this.options_ = {};
        95
        96 /** @private {(webdriver.ProxyConfig|null)} */
        97 this.proxy_ = null;
        98};
        99
        100
        101
        102/**
        103 * Extracts the IEDriver specific options from the given capabilities
        104 * object.
        105 * @param {!webdriver.Capabilities} capabilities The capabilities object.
        106 * @return {!Options} The IEDriver options.
        107 */
        108Options.fromCapabilities = function(capabilities) {
        109 var options = new Options();
        110 var map = options.options_;
        111
        112 Object.keys(Key).forEach(function(key) {
        113 key = Key[key];
        114 if (capabilities.has(key)) {
        115 map[key] = capabilities.get(key);
        116 }
        117 });
        118
        119 if (capabilities.has(webdriver.Capability.PROXY)) {
        120 options.setProxy(capabilities.get(webdriver.Capability.PROXY));
        121 }
        122
        123 return options;
        124};
        125
        126
        127/**
        128 * Whether to disable the protected mode settings check when the session is
        129 * created. Disbling this setting may lead to significant instability as the
        130 * browser may become unresponsive/hang. Only "best effort" support is provided
        131 * when using this capability.
        132 *
        133 * For more information, refer to the IEDriver's
        134 * [required system configuration](http://goo.gl/eH0Yi3).
        135 *
        136 * @param {boolean} ignoreSettings Whether to ignore protected mode settings.
        137 * @return {!Options} A self reference.
        138 */
        139Options.prototype.introduceFlakinessByIgnoringProtectedModeSettings =
        140 function(ignoreSettings) {
        141 this.options_[Key.IGNORE_PROTECTED_MODE_SETTINGS] = !!ignoreSettings;
        142 return this;
        143 };
        144
        145
        146/**
        147 * Indicates whether to skip the check that the browser's zoom level is set to
        148 * 100%.
        149 *
        150 * @parm {boolean} ignore Whether to ignore the browser's zoom level settings.
        151 * @return {!Options} A self reference.
        152 */
        153Options.prototype.ignoreZoomSetting = function(ignore) {
        154 this.options_[Key.IGNORE_ZOOM_SETTING] = !!ignore;
        155 return this;
        156};
        157
        158
        159/**
        160 * Sets the initial URL loaded when IE starts. This is intended to be used with
        161 * {@link #ignoreProtectedModeSettings} to allow the user to initialize IE in
        162 * the proper Protected Mode zone. Setting this option may cause browser
        163 * instability or flaky and unresponsive code. Only "best effort" support is
        164 * provided when using this option.
        165 *
        166 * @param {string} url The initial browser URL.
        167 * @return {!Options} A self reference.
        168 */
        169Options.prototype.initialBrowserUrl = function(url) {
        170 this.options_[Key.INITIAL_BROWSER_URL] = url;
        171 return this;
        172};
        173
        174
        175/**
        176 * Configures whether to enable persistent mouse hovering (true by default).
        177 * Persistent hovering is achieved by continuously firing mouse over events at
        178 * the last location the mouse cursor has been moved to.
        179 *
        180 * @param {boolean} enable Whether to enable persistent hovering.
        181 * @return {!Options} A self reference.
        182 */
        183Options.prototype.enablePersistentHover = function(enable) {
        184 this.options_[Key.ENABLE_PERSISTENT_HOVER] = !!enable;
        185 return this;
        186};
        187
        188
        189/**
        190 * Configures whether the driver should attempt to remove obsolete
        191 * {@linkplain webdriver.WebElement WebElements} from its internal cache on
        192 * page navigation (true by default). Disabling this option will cause the
        193 * driver to run with a larger memory footprint.
        194 *
        195 * @param {boolean} enable Whether to enable element reference cleanup.
        196 * @return {!Options} A self reference.
        197 */
        198Options.prototype.enableElementCacheCleanup = function(enable) {
        199 this.options_[Key.ENABLE_ELEMENT_CACHE_CLEANUP] = !!enable;
        200 return this;
        201};
        202
        203
        204/**
        205 * Configures whether to require the IE window to have input focus before
        206 * performing any user interactions (i.e. mouse or keyboard events). This
        207 * option is disabled by default, but delivers much more accurate interaction
        208 * events when enabled.
        209 *
        210 * @param {boolean} require Whether to require window focus.
        211 * @return {!Options} A self reference.
        212 */
        213Options.prototype.requireWindowFocus = function(require) {
        214 this.options_[Key.REQUIRE_WINDOW_FOCUS] = !!require;
        215 return this;
        216};
        217
        218
        219/**
        220 * Configures the timeout, in milliseconds, that the driver will attempt to
        221 * located and attach to a newly opened instance of Internet Explorer. The
        222 * default is zero, which indicates waiting indefinitely.
        223 *
        224 * @param {number} timeout How long to wait for IE.
        225 * @return {!Options} A self reference.
        226 */
        227Options.prototype.browserAttachTimeout = function(timeout) {
        228 this.options_[Key.BROWSER_ATTACH_TIMEOUT] = Math.max(timeout, 0);
        229 return this;
        230};
        231
        232
        233/**
        234 * Configures whether to launch Internet Explorer using the CreateProcess API.
        235 * If this option is not specified, IE is launched using IELaunchURL, if
        236 * available. For IE 8 and above, this option requires the TabProcGrowth
        237 * registry value to be set to 0.
        238 *
        239 * @param {boolean} force Whether to use the CreateProcess API.
        240 * @return {!Options} A self reference.
        241 */
        242Options.prototype.forceCreateProcessApi = function(force) {
        243 this.options_[Key.FORCE_CREATE_PROCESS] = !!force;
        244 return this;
        245};
        246
        247
        248/**
        249 * Specifies command-line switches to use when launching Internet Explorer.
        250 * This is only valid when used with {@link #forceCreateProcessApi}.
        251 *
        252 * @param {...(string|!Array.<string>)} var_args The arguments to add.
        253 * @return {!Options} A self reference.
        254 */
        255Options.prototype.addArguments = function(var_args) {
        256 var args = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || [];
        257 args = args.concat.apply(args, arguments);
        258 this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = args;
        259 return this;
        260};
        261
        262
        263/**
        264 * Configures whether proxies should be configured on a per-process basis. If
        265 * not set, setting a {@linkplain #setProxy proxy} will configure the system
        266 * proxy. The default behavior is to use the system proxy.
        267 *
        268 * @param {boolean} enable Whether to enable per-process proxy settings.
        269 * @return {!Options} A self reference.
        270 */
        271Options.prototype.usePerProcessProxy = function(enable) {
        272 this.options_[Key.USE_PER_PROCESS_PROXY] = !!enable;
        273 return this;
        274};
        275
        276
        277/**
        278 * Configures whether to clear the cache, cookies, history, and saved form data
        279 * before starting the browser. _Using this capability will clear session data
        280 * for all running instances of Internet Explorer, including those started
        281 * manually._
        282 *
        283 * @param {boolean} cleanSession Whether to clear all session data on startup.
        284 * @return {!Options} A self reference.
        285 */
        286Options.prototype.ensureCleanSession = function(cleanSession) {
        287 this.options_[Key.ENSURE_CLEAN_SESSION] = !!cleanSession;
        288 return this;
        289};
        290
        291
        292/**
        293 * Sets the path to the log file the driver should log to.
        294 * @param {string} path The log file path.
        295 * @return {!Options} A self reference.
        296 */
        297Options.prototype.setLogFile = function(file) {
        298 this.options_[Key.LOG_FILE] = file;
        299 return this;
        300};
        301
        302
        303/**
        304 * Sets the IEDriverServer's logging {@linkplain Level level}.
        305 * @param {Level} level The logging level.
        306 * @return {!Options} A self reference.
        307 */
        308Options.prototype.setLogLevel = function(level) {
        309 this.options_[Key.LOG_LEVEL] = level;
        310 return this;
        311};
        312
        313
        314/**
        315 * Sets the IP address of the driver's host adapter.
        316 * @param {string} host The IP address to use.
        317 * @return {!Options} A self reference.
        318 */
        319Options.prototype.setHost = function(host) {
        320 this.options_[Key.HOST] = host;
        321 return this;
        322};
        323
        324
        325/**
        326 * Sets the path of the temporary data directory to use.
        327 * @param {string} path The log file path.
        328 * @return {!Options} A self reference.
        329 */
        330Options.prototype.setExtractPath = function(path) {
        331 this.options_[Key.EXTRACT_PATH] = path;
        332 return this;
        333};
        334
        335
        336/**
        337 * Sets whether the driver should start in silent mode.
        338 * @param {boolean} silent Whether to run in silent mode.
        339 * @return {!Options} A self reference.
        340 */
        341Options.prototype.silent = function(silent) {
        342 this.options_[Key.SILENT] = silent;
        343 return this;
        344};
        345
        346
        347/**
        348 * Sets the proxy settings for the new session.
        349 * @param {webdriver.ProxyConfig} proxy The proxy configuration to use.
        350 * @return {!Options} A self reference.
        351 */
        352Options.prototype.setProxy = function(proxy) {
        353 this.proxy_ = proxy;
        354 return this;
        355};
        356
        357
        358/**
        359 * Converts this options instance to a {@link webdriver.Capabilities} object.
        360 * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge
        361 * these options into, if any.
        362 * @return {!webdriver.Capabilities} The capabilities.
        363 */
        364Options.prototype.toCapabilities = function(opt_capabilities) {
        365 var capabilities = opt_capabilities || webdriver.Capabilities.ie();
        366 if (this.proxy_) {
        367 capabilities.set(webdriver.Capability.PROXY, this.proxy_);
        368 }
        369 Object.keys(this.options_).forEach(function(key) {
        370 capabilities.set(key, this.options_[key]);
        371 }, this);
        372 return capabilities;
        373};
        374
        375
        376function createServiceFromCapabilities(capabilities) {
        377 if (process.platform !== 'win32') {
        378 throw Error(
        379 'The IEDriver may only be used on Windows, but you appear to be on ' +
        380 process.platform + '. Did you mean to run against a remote ' +
        381 'WebDriver server?');
        382 }
        383
        384 var exe = io.findInPath(IEDRIVER_EXE, true);
        385 if (!fs.existsSync(exe)) {
        386 throw Error('File does not exist: ' + exe);
        387 }
        388
        389 var args = [];
        390 if (capabilities.has(Key.HOST)) {
        391 args.push('--host=' + capabilities.get(Key.HOST));
        392 }
        393 if (capabilities.has(Key.LOG_FILE)) {
        394 args.push('--log-file=' + capabilities.get(Key.LOG_FILE));
        395 }
        396 if (capabilities.has(Key.LOG_LEVEL)) {
        397 args.push('--log-level=' + capabilities.get(Key.LOG_LEVEL));
        398 }
        399 if (capabilities.has(Key.EXTRACT_PATH)) {
        400 args.push('--extract-path=' + capabilities.get(Key.EXTRACT_PATH));
        401 }
        402 if (capabilities.get(Key.SILENT)) {
        403 args.push('--silent');
        404 }
        405
        406 var port = portprober.findFreePort();
        407 return new remote.DriverService(exe, {
        408 loopback: true,
        409 port: port,
        410 args: port.then(function(port) {
        411 return args.concat('--port=' + port);
        412 }),
        413 stdio: 'ignore'
        414 });
        415}
        416
        417
        418/**
        419 * A WebDriver client for Microsoft's Internet Explorer.
        420 *
        421 * @param {(webdriver.Capabilities|Options)=} opt_config The configuration
        422 * options.
        423 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
        424 * {@code null} to use the currently active flow.
        425 * @constructor
        426 * @extends {webdriver.WebDriver}
        427 */
        428var Driver = function(opt_config, opt_flow) {
        429 var capabilities = opt_config instanceof Options ?
        430 opt_config.toCapabilities() :
        431 (opt_config || webdriver.Capabilities.ie());
        432
        433 var service = createServiceFromCapabilities(capabilities);
        434 var executor = executors.createExecutor(service.start());
        435 var driver = webdriver.WebDriver.createSession(
        436 executor, capabilities, opt_flow);
        437
        438 webdriver.WebDriver.call(
        439 this, driver.getSession(), executor, driver.controlFlow());
        440
        441 var boundQuit = this.quit.bind(this);
        442
        443 /** @override */
        444 this.quit = function() {
        445 return boundQuit().thenFinally(service.kill.bind(service));
        446 };
        447};
        448util.inherits(Driver, webdriver.WebDriver);
        449
        450
        451/**
        452 * This function is a no-op as file detectors are not supported by this
        453 * implementation.
        454 * @override
        455 */
        456Driver.prototype.setFileDetector = function() {
        457};
        458
        459
        460// PUBLIC API
        461
        462
        463exports.Driver = Driver;
        464exports.Options = Options;
        465exports.Level = Level;
        \ No newline at end of file diff --git a/docs/source/index.js.src.html b/docs/source/index.js.src.html index b4cc5c6..9c22c9d 100644 --- a/docs/source/index.js.src.html +++ b/docs/source/index.js.src.html @@ -1 +1 @@ -index.js

        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
        21var base = require('./_base');
        22var builder = require('./builder');
        23var 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)} */
        32exports.ActionSequence = base.require('webdriver.ActionSequence');
        33
        34
        35/** @type {function(new: builder.Builder)} */
        36exports.Builder = builder.Builder;
        37
        38
        39/** @type {webdriver.By.} */
        40exports.By = base.require('webdriver.By');
        41
        42
        43/** @type {function(new: webdriver.Capabilities)} */
        44exports.Capabilities = base.require('webdriver.Capabilities');
        45
        46
        47/** @type {function(new: webdriver.Command)} */
        48exports.Command = base.require('webdriver.Command');
        49
        50
        51/** @type {function(new: webdriver.EventEmitter)} */
        52exports.EventEmitter = base.require('webdriver.EventEmitter');
        53
        54
        55/** @type {function(new: webdriver.Session)} */
        56exports.Session = base.require('webdriver.Session');
        57
        58
        59/** @type {function(new: webdriver.WebDriver)} */
        60exports.WebDriver = base.require('webdriver.WebDriver');
        61
        62
        63/** @type {function(new: webdriver.WebElement)} */
        64exports.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}));
        \ No newline at end of file +index.js

        index.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview The main user facing module. Exports WebDriver's primary
        20 * public API and provides convenience assessors to certain sub-modules.
        21 */
        22
        23var base = require('./_base');
        24var builder = require('./builder');
        25var error = require('./error');
        26
        27
        28// NOTE: the remainder of this file is nasty and verbose, but the annotations
        29// are necessary to guide the Closure Compiler's type analysis. Without them,
        30// we would not be able to extract any meaningful API documentation.
        31
        32
        33/** @type {function(new: webdriver.ActionSequence)} */
        34exports.ActionSequence = base.require('webdriver.ActionSequence');
        35
        36
        37/** @type {function(new: builder.Builder)} */
        38exports.Builder = builder.Builder;
        39
        40
        41/** @type {webdriver.By.} */
        42exports.By = base.require('webdriver.By');
        43
        44
        45/** @type {function(new: webdriver.Capabilities)} */
        46exports.Capabilities = base.require('webdriver.Capabilities');
        47
        48
        49/** @type {function(new: webdriver.Command)} */
        50exports.Command = base.require('webdriver.Command');
        51
        52
        53/** @type {function(new: webdriver.EventEmitter)} */
        54exports.EventEmitter = base.require('webdriver.EventEmitter');
        55
        56
        57/** @type {function(new: webdriver.FileDetector)} */
        58exports.FileDetector = base.require('webdriver.FileDetector');
        59
        60
        61/** @type {function(new: webdriver.Serializable)} */
        62exports.Serializable = base.require('webdriver.Serializable');
        63
        64
        65/** @type {function(new: webdriver.Session)} */
        66exports.Session = base.require('webdriver.Session');
        67
        68
        69/** @type {function(new: webdriver.WebDriver)} */
        70exports.WebDriver = base.require('webdriver.WebDriver');
        71
        72
        73/** @type {function(new: webdriver.WebElement)} */
        74exports.WebElement = base.require('webdriver.WebElement');
        75
        76
        77/** @type {function(new: webdriver.WebElementPromise)} */
        78exports.WebElementPromise = base.require('webdriver.WebElementPromise');
        79
        80
        81// Export the remainder of our API through getters to keep things cleaner
        82// when this module is used in a REPL environment.
        83
        84
        85/** @type {webdriver.Browser.} */
        86(exports.__defineGetter__('Browser', function() {
        87 return base.require('webdriver.Browser');
        88}));
        89
        90
        91/** @type {webdriver.Button.} */
        92(exports.__defineGetter__('Button', function() {
        93 return base.require('webdriver.Button');
        94}));
        95
        96
        97/** @type {webdriver.Capability.} */
        98(exports.__defineGetter__('Capability', function() {
        99 return base.require('webdriver.Capability');
        100}));
        101
        102
        103/** @type {webdriver.CommandName.} */
        104(exports.__defineGetter__('CommandName', function() {
        105 return base.require('webdriver.CommandName');
        106}));
        107
        108
        109/** @type {webdriver.Key.} */
        110(exports.__defineGetter__('Key', function() {
        111 return base.require('webdriver.Key');
        112}));
        113
        114
        115/** @type {error.} */
        116(exports.__defineGetter__('error', function() {
        117 return error;
        118}));
        119
        120
        121/** @type {error.} */
        122(exports.__defineGetter__('error', function() {
        123 return error;
        124}));
        125
        126
        127/** @type {webdriver.logging.} */
        128(exports.__defineGetter__('logging', function() {
        129 return base.exportPublicApi('webdriver.logging');
        130}));
        131
        132
        133/** @type {webdriver.promise.} */
        134(exports.__defineGetter__('promise', function() {
        135 return base.exportPublicApi('webdriver.promise');
        136}));
        137
        138
        139/** @type {webdriver.stacktrace.} */
        140(exports.__defineGetter__('stacktrace', function() {
        141 return base.exportPublicApi('webdriver.stacktrace');
        142}));
        143
        144
        145/** @type {webdriver.until.} */
        146(exports.__defineGetter__('until', function() {
        147 return base.exportPublicApi('webdriver.until');
        148}));
        \ No newline at end of file diff --git a/docs/source/io/exec.js.src.html b/docs/source/io/exec.js.src.html new file mode 100644 index 0000000..9063c7e --- /dev/null +++ b/docs/source/io/exec.js.src.html @@ -0,0 +1 @@ +exec.js

        io/exec.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18'use strict';
        19
        20var childProcess = require('child_process');
        21
        22var promise = require('..').promise;
        23
        24
        25/**
        26 * A hash with configuration options for an executed command.
        27 *
        28 * - `args` - Command line arguments.
        29 * - `env` - Command environment; will inherit from the current process if
        30 * missing.
        31 * - `stdio` - IO configuration for the spawned server process. For more
        32 * information, refer to the documentation of `child_process.spawn`.
        33 *
        34 * @typedef {{
        35 * args: (!Array.<string>|undefined),
        36 * env: (!Object.<string, string>|undefined),
        37 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
        38 * }}
        39 */
        40var Options;
        41
        42
        43/**
        44 * Describes a command's termination conditions.
        45 * @param {?number} code The exit code, or {@code null} if the command did not
        46 * exit normally.
        47 * @param {?string} signal The signal used to kill the command, or
        48 * {@code null}.
        49 * @constructor
        50 */
        51var Result = function(code, signal) {
        52 /** @type {?number} */
        53 this.code = code;
        54
        55 /** @type {?string} */
        56 this.signal = signal;
        57};
        58
        59
        60/** @override */
        61Result.prototype.toString = function() {
        62 return 'Result(code=' + this.code + ', signal=' + this.signal + ')';
        63};
        64
        65
        66
        67/**
        68 * Represents a command running in a sub-process.
        69 * @param {!promise.Promise.<!Result>} result The command result.
        70 * @constructor
        71 */
        72var Command = function(result, onKill) {
        73 /** @return {boolean} Whether this command is still running. */
        74 this.isRunning = function() {
        75 return result.isPending();
        76 };
        77
        78 /**
        79 * @return {!promise.Promise.<!Result>} A promise for the result of this
        80 * command.
        81 */
        82 this.result = function() {
        83 return result;
        84 };
        85
        86 /**
        87 * Sends a signal to the underlying process.
        88 * @param {string=} opt_signal The signal to send; defaults to
        89 * {@code SIGTERM}.
        90 */
        91 this.kill = function(opt_signal) {
        92 onKill(opt_signal || 'SIGTERM');
        93 };
        94};
        95
        96
        97// PUBLIC API
        98
        99
        100/**
        101 * Spawns a child process. The returned {@link Command} may be used to wait
        102 * for the process result or to send signals to the process.
        103 *
        104 * @param {string} command The executable to spawn.
        105 * @param {Options=} opt_options The command options.
        106 * @return {!Command} The launched command.
        107 */
        108module.exports = function(command, opt_options) {
        109 var options = opt_options || {};
        110
        111 var proc = childProcess.spawn(command, options.args || [], {
        112 env: options.env || process.env,
        113 stdio: options.stdio || 'ignore'
        114 }).once('exit', onExit);
        115
        116 // This process should not wait on the spawned child, however, we do
        117 // want to ensure the child is killed when this process exits.
        118 proc.unref();
        119 process.once('exit', killCommand);
        120
        121 var result = promise.defer();
        122 var cmd = new Command(result.promise, function(signal) {
        123 if (!result.isPending() || !proc) {
        124 return; // No longer running.
        125 }
        126 proc.kill(signal);
        127 });
        128 return cmd;
        129
        130 function onExit(code, signal) {
        131 proc = null;
        132 process.removeListener('exit', killCommand);
        133 result.fulfill(new Result(code, signal));
        134 }
        135
        136 function killCommand() {
        137 process.removeListener('exit', killCommand);
        138 proc && proc.kill('SIGTERM');
        139 }
        140};
        \ No newline at end of file diff --git a/docs/source/io/index.js.src.html b/docs/source/io/index.js.src.html index 0a18b2b..c2a0f4c 100644 --- a/docs/source/io/index.js.src.html +++ b/docs/source/io/index.js.src.html @@ -1 +1 @@ -index.js

        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
        16var fs = require('fs'),
        17 path = require('path');
        18
        19
        20var 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 */
        35exports.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};
        \ No newline at end of file +index.js

        io/index.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18var fs = require('fs'),
        19 path = require('path'),
        20 rimraf = require('rimraf'),
        21 tmp = require('tmp');
        22
        23var promise = require('..').promise;
        24
        25
        26
        27// PUBLIC API
        28
        29
        30
        31/**
        32 * Recursively removes a directory and all of its contents. This is equivalent
        33 * to {@code rm -rf} on a POSIX system.
        34 * @param {string} path Path to the directory to remove.
        35 * @return {!promise.Promise} A promise to be resolved when the operation has
        36 * completed.
        37 */
        38exports.rmDir = function(path) {
        39 return new promise.Promise(function(fulfill, reject) {
        40 var numAttempts = 0;
        41 attemptRm();
        42 function attemptRm() {
        43 numAttempts += 1;
        44 rimraf(path, function(err) {
        45 if (err) {
        46 if (err.code === 'ENOTEMPTY' && numAttempts < 2) {
        47 attemptRm();
        48 return;
        49 }
        50 reject(err);
        51 } else {
        52 fulfill();
        53 }
        54 });
        55 }
        56 });
        57};
        58
        59
        60/**
        61 * Copies one file to another.
        62 * @param {string} src The source file.
        63 * @param {string} dst The destination file.
        64 * @return {!promise.Promise.<string>} A promise for the copied file's path.
        65 */
        66exports.copy = function(src, dst) {
        67 var copied = promise.defer();
        68
        69 var rs = fs.createReadStream(src);
        70 rs.on('error', copied.reject);
        71 rs.on('end', function() {
        72 copied.fulfill(dst);
        73 });
        74
        75 var ws = fs.createWriteStream(dst);
        76 ws.on('error', copied.reject);
        77
        78 rs.pipe(ws);
        79
        80 return copied.promise;
        81};
        82
        83
        84/**
        85 * Recursively copies the contents of one directory to another.
        86 * @param {string} src The source directory to copy.
        87 * @param {string} dst The directory to copy into.
        88 * @param {(RegEx|function(string): boolean)=} opt_exclude An exclusion filter
        89 * as either a regex or predicate function. All files matching this filter
        90 * will not be copied.
        91 * @return {!promise.Promise.<string>} A promise for the destination
        92 * directory's path once all files have been copied.
        93 */
        94exports.copyDir = function(src, dst, opt_exclude) {
        95 var predicate = opt_exclude;
        96 if (opt_exclude && typeof opt_exclude !== 'function') {
        97 predicate = function(p) {
        98 return !opt_exclude.test(p);
        99 };
        100 }
        101
        102 // TODO(jleyba): Make this function completely async.
        103 if (!fs.existsSync(dst)) {
        104 fs.mkdirSync(dst);
        105 }
        106
        107 var files = fs.readdirSync(src);
        108 files = files.map(function(file) {
        109 return path.join(src, file);
        110 });
        111
        112 if (predicate) {
        113 files = files.filter(predicate);
        114 }
        115
        116 var results = [];
        117 files.forEach(function(file) {
        118 var stats = fs.statSync(file);
        119 var target = path.join(dst, path.basename(file));
        120
        121 if (stats.isDirectory()) {
        122 if (!fs.existsSync(target)) {
        123 fs.mkdirSync(target, stats.mode);
        124 }
        125 results.push(exports.copyDir(file, target, predicate));
        126 } else {
        127 results.push(exports.copy(file, target));
        128 }
        129 });
        130
        131 return promise.all(results).then(function() {
        132 return dst;
        133 });
        134};
        135
        136
        137/**
        138 * Tests if a file path exists.
        139 * @param {string} path The path to test.
        140 * @return {!promise.Promise.<boolean>} A promise for whether the file exists.
        141 */
        142exports.exists = function(path) {
        143 var result = promise.defer();
        144 fs.exists(path, result.fulfill);
        145 return result.promise;
        146};
        147
        148
        149/**
        150 * Deletes a name from the filesystem and possibly the file it refers to. Has
        151 * no effect if the file does not exist.
        152 * @param {string} path The path to remove.
        153 * @return {!promise.Promise} A promise for when the file has been removed.
        154 */
        155exports.unlink = function(path) {
        156 return new promise.Promise(function(fulfill, reject) {
        157 fs.exists(path, function(exists) {
        158 if (exists) {
        159 fs.unlink(path, function(err) {
        160 err && reject(err) || fulfill();
        161 });
        162 } else {
        163 fulfill();
        164 }
        165 });
        166 });
        167};
        168
        169
        170/**
        171 * @return {!promise.Promise.<string>} A promise for the path to a temporary
        172 * directory.
        173 * @see https://www.npmjs.org/package/tmp
        174 */
        175exports.tmpDir = function() {
        176 return promise.checkedNodeCall(tmp.dir);
        177};
        178
        179
        180/**
        181 * @param {{postfix: string}=} opt_options Temporary file options.
        182 * @return {!promise.Promise.<string>} A promise for the path to a temporary
        183 * file.
        184 * @see https://www.npmjs.org/package/tmp
        185 */
        186exports.tmpFile = function(opt_options) {
        187 // |tmp.file| checks arguments length to detect options rather than doing a
        188 // truthy check, so we must only pass options if there are some to pass.
        189 return opt_options ?
        190 promise.checkedNodeCall(tmp.file, opt_options) :
        191 promise.checkedNodeCall(tmp.file);
        192};
        193
        194
        195/**
        196 * Searches the {@code PATH} environment variable for the given file.
        197 * @param {string} file The file to locate on the PATH.
        198 * @param {boolean=} opt_checkCwd Whether to always start with the search with
        199 * the current working directory, regardless of whether it is explicitly
        200 * listed on the PATH.
        201 * @return {?string} Path to the located file, or {@code null} if it could
        202 * not be found.
        203 */
        204exports.findInPath = function(file, opt_checkCwd) {
        205 if (opt_checkCwd) {
        206 var tmp = path.join(process.cwd(), file);
        207 if (fs.existsSync(tmp)) {
        208 return tmp;
        209 }
        210 }
        211
        212 var dirs = process.env['PATH'].split(path.delimiter);
        213 var found = null;
        214 dirs.forEach(function(dir) {
        215 var tmp = path.join(dir, file);
        216 if (!found && fs.existsSync(tmp)) {
        217 found = tmp;
        218 }
        219 });
        220 return found;
        221};
        \ No newline at end of file diff --git a/docs/source/lib/atoms/error.js.src.html b/docs/source/lib/atoms/error.js.src.html index b9696c3..201fcac 100644 --- a/docs/source/lib/atoms/error.js.src.html +++ b/docs/source/lib/atoms/error.js.src.html @@ -1 +1 @@ -error.js

        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
        21goog.provide('bot.Error');
        22goog.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 */
        31bot.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 */
        77bot.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};
        115goog.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 */
        123bot.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 */
        156bot.Error.CODE_TO_STATE_ = {};
        157goog.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 */
        197bot.Error.prototype.isAutomationError = true;
        198
        199
        200if (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}
        \ No newline at end of file +error.js

        lib/atoms/error.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Utilities for working with errors as defined by WebDriver's
        20 * wire protocol: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
        21 */
        22
        23goog.provide('bot.Error');
        24goog.provide('bot.ErrorCode');
        25
        26
        27/**
        28 * Error codes from the Selenium WebDriver protocol:
        29 * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#response-status-codes
        30 *
        31 * @enum {number}
        32 */
        33bot.ErrorCode = {
        34 SUCCESS: 0, // Included for completeness
        35
        36 NO_SUCH_ELEMENT: 7,
        37 NO_SUCH_FRAME: 8,
        38 UNKNOWN_COMMAND: 9,
        39 UNSUPPORTED_OPERATION: 9, // Alias.
        40 STALE_ELEMENT_REFERENCE: 10,
        41 ELEMENT_NOT_VISIBLE: 11,
        42 INVALID_ELEMENT_STATE: 12,
        43 UNKNOWN_ERROR: 13,
        44 ELEMENT_NOT_SELECTABLE: 15,
        45 JAVASCRIPT_ERROR: 17,
        46 XPATH_LOOKUP_ERROR: 19,
        47 TIMEOUT: 21,
        48 NO_SUCH_WINDOW: 23,
        49 INVALID_COOKIE_DOMAIN: 24,
        50 UNABLE_TO_SET_COOKIE: 25,
        51 UNEXPECTED_ALERT_OPEN: 26,
        52 NO_SUCH_ALERT: 27,
        53 SCRIPT_TIMEOUT: 28,
        54 INVALID_ELEMENT_COORDINATES: 29,
        55 IME_NOT_AVAILABLE: 30,
        56 IME_ENGINE_ACTIVATION_FAILED: 31,
        57 INVALID_SELECTOR_ERROR: 32,
        58 SESSION_NOT_CREATED: 33,
        59 MOVE_TARGET_OUT_OF_BOUNDS: 34,
        60 SQL_DATABASE_ERROR: 35,
        61 INVALID_XPATH_SELECTOR: 51,
        62 INVALID_XPATH_SELECTOR_RETURN_TYPE: 52,
        63 // The following error codes are derived straight from HTTP return codes.
        64 METHOD_NOT_ALLOWED: 405
        65};
        66
        67
        68/**
        69 * Represents an error returned from a WebDriver command request.
        70 *
        71 * @param {!bot.ErrorCode} code The error's status code.
        72 * @param {string=} opt_message Optional error message.
        73 * @constructor
        74 * @extends {Error}
        75 */
        76bot.Error = function(code, opt_message) {
        77
        78 /**
        79 * This error's status code.
        80 * @type {!bot.ErrorCode}
        81 */
        82 this.code = code;
        83
        84 /** @type {string} */
        85 this.state =
        86 bot.Error.CODE_TO_STATE_[code] || bot.Error.State.UNKNOWN_ERROR;
        87
        88 /** @override */
        89 this.message = opt_message || '';
        90
        91 var name = this.state.replace(/((?:^|\s+)[a-z])/g, function(str) {
        92 // IE<9 does not support String#trim(). Also, IE does not include 0xa0
        93 // (the non-breaking-space) in the \s character class, so we have to
        94 // explicitly include it.
        95 return str.toUpperCase().replace(/^[\s\xa0]+/g, '');
        96 });
        97
        98 var l = name.length - 'Error'.length;
        99 if (l < 0 || name.indexOf('Error', l) != l) {
        100 name += 'Error';
        101 }
        102
        103 /** @override */
        104 this.name = name;
        105
        106 // Generate a stacktrace for our custom error; ensure the error has our
        107 // custom name and message so the stack prints correctly in all browsers.
        108 var template = new Error(this.message);
        109 template.name = this.name;
        110
        111 /** @override */
        112 this.stack = template.stack || '';
        113};
        114goog.inherits(bot.Error, Error);
        115
        116
        117/**
        118 * Status strings enumerated in the W3C WebDriver protocol.
        119 * @enum {string}
        120 * @see https://w3c.github.io/webdriver/webdriver-spec.html#handling-errors
        121 */
        122bot.Error.State = {
        123 ELEMENT_NOT_SELECTABLE: 'element not selectable',
        124 ELEMENT_NOT_VISIBLE: 'element not visible',
        125 INVALID_ARGUMENT: 'invalid argument',
        126 INVALID_COOKIE_DOMAIN: 'invalid cookie domain',
        127 INVALID_ELEMENT_COORDINATES: 'invalid element coordinates',
        128 INVALID_ELEMENT_STATE: 'invalid element state',
        129 INVALID_SELECTOR: 'invalid selector',
        130 INVALID_SESSION_ID: 'invalid session id',
        131 JAVASCRIPT_ERROR: 'javascript error',
        132 MOVE_TARGET_OUT_OF_BOUNDS: 'move target out of bounds',
        133 NO_SUCH_ALERT: 'no such alert',
        134 NO_SUCH_ELEMENT: 'no such element',
        135 NO_SUCH_FRAME: 'no such frame',
        136 NO_SUCH_WINDOW: 'no such window',
        137 SCRIPT_TIMEOUT: 'script timeout',
        138 SESSION_NOT_CREATED: 'session not created',
        139 STALE_ELEMENT_REFERENCE: 'stale element reference',
        140 TIMEOUT: 'timeout',
        141 UNABLE_TO_SET_COOKIE: 'unable to set cookie',
        142 UNEXPECTED_ALERT_OPEN: 'unexpected alert open',
        143 UNKNOWN_COMMAND: 'unknown command',
        144 UNKNOWN_ERROR: 'unknown error',
        145 UNKNOWN_METHOD: 'unknown method',
        146 UNSUPPORTED_OPERATION: 'unsupported operation'
        147};
        148
        149
        150/**
        151 * A map of error codes to state string.
        152 * @private {!Object.<bot.ErrorCode, bot.Error.State>}
        153 */
        154bot.Error.CODE_TO_STATE_ = {};
        155goog.scope(function() {
        156 var map = bot.Error.CODE_TO_STATE_;
        157 var code = bot.ErrorCode;
        158 var state = bot.Error.State;
        159
        160 map[code.ELEMENT_NOT_SELECTABLE] = state.ELEMENT_NOT_SELECTABLE;
        161 map[code.ELEMENT_NOT_VISIBLE] = state.ELEMENT_NOT_VISIBLE;
        162 map[code.IME_ENGINE_ACTIVATION_FAILED] = state.UNKNOWN_ERROR;
        163 map[code.IME_NOT_AVAILABLE] = state.UNKNOWN_ERROR;
        164 map[code.INVALID_COOKIE_DOMAIN] = state.INVALID_COOKIE_DOMAIN;
        165 map[code.INVALID_ELEMENT_COORDINATES] = state.INVALID_ELEMENT_COORDINATES;
        166 map[code.INVALID_ELEMENT_STATE] = state.INVALID_ELEMENT_STATE;
        167 map[code.INVALID_SELECTOR_ERROR] = state.INVALID_SELECTOR;
        168 map[code.INVALID_XPATH_SELECTOR] = state.INVALID_SELECTOR;
        169 map[code.INVALID_XPATH_SELECTOR_RETURN_TYPE] = state.INVALID_SELECTOR;
        170 map[code.JAVASCRIPT_ERROR] = state.JAVASCRIPT_ERROR;
        171 map[code.METHOD_NOT_ALLOWED] = state.UNSUPPORTED_OPERATION;
        172 map[code.MOVE_TARGET_OUT_OF_BOUNDS] = state.MOVE_TARGET_OUT_OF_BOUNDS;
        173 map[code.NO_SUCH_ALERT] = state.NO_SUCH_ALERT;
        174 map[code.NO_SUCH_ELEMENT] = state.NO_SUCH_ELEMENT;
        175 map[code.NO_SUCH_FRAME] = state.NO_SUCH_FRAME;
        176 map[code.NO_SUCH_WINDOW] = state.NO_SUCH_WINDOW;
        177 map[code.SCRIPT_TIMEOUT] = state.SCRIPT_TIMEOUT;
        178 map[code.SESSION_NOT_CREATED] = state.SESSION_NOT_CREATED;
        179 map[code.STALE_ELEMENT_REFERENCE] = state.STALE_ELEMENT_REFERENCE;
        180 map[code.TIMEOUT] = state.TIMEOUT;
        181 map[code.UNABLE_TO_SET_COOKIE] = state.UNABLE_TO_SET_COOKIE;
        182 map[code.UNEXPECTED_ALERT_OPEN] = state.UNEXPECTED_ALERT_OPEN
        183 map[code.UNKNOWN_ERROR] = state.UNKNOWN_ERROR;
        184 map[code.UNSUPPORTED_OPERATION] = state.UNKNOWN_COMMAND;
        185}); // goog.scope
        186
        187
        188/**
        189 * Flag used for duck-typing when this code is embedded in a Firefox extension.
        190 * This is required since an Error thrown in one component and then reported
        191 * to another will fail instanceof checks in the second component.
        192 * @type {boolean}
        193 */
        194bot.Error.prototype.isAutomationError = true;
        195
        196
        197if (goog.DEBUG) {
        198 /** @return {string} The string representation of this error. */
        199 bot.Error.prototype.toString = function() {
        200 return this.name + ': ' + this.message;
        201 };
        202}
        \ No newline at end of file diff --git a/docs/source/lib/atoms/json.js.src.html b/docs/source/lib/atoms/json.js.src.html index a585c2f..7f38fbe 100644 --- a/docs/source/lib/atoms/json.js.src.html +++ b/docs/source/lib/atoms/json.js.src.html @@ -1 +1 @@ -json.js

        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
        21goog.provide('bot.json');
        22
        23goog.require('bot.userAgent');
        24goog.require('goog.json');
        25goog.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 */
        40bot.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 */
        49bot.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 */
        67bot.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 */
        77bot.json.parse = bot.json.NATIVE_JSON && bot.json.SUPPORTS_NATIVE_JSON_ ?
        78 JSON.parse : goog.json.parse;
        \ No newline at end of file +json.js

        lib/atoms/json.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Provides JSON utilities that uses native JSON parsing where
        20 * possible (a feature not currently offered by Closure).
        21 */
        22
        23goog.provide('bot.json');
        24
        25goog.require('bot.userAgent');
        26goog.require('goog.json');
        27goog.require('goog.userAgent');
        28
        29
        30/**
        31 * @define {boolean} NATIVE_JSON indicates whether the code should rely on the
        32 * native {@code JSON} functions, if available.
        33 *
        34 * <p>The JSON functions can be defined by external libraries like Prototype
        35 * and setting this flag to false forces the use of Closure's goog.json
        36 * implementation.
        37 *
        38 * <p>If your JavaScript can be loaded by a third_party site and you are wary
        39 * about relying on the native functions, specify
        40 * "--define bot.json.NATIVE_JSON=false" to the Closure compiler.
        41 */
        42bot.json.NATIVE_JSON = true;
        43
        44
        45/**
        46 * Whether the current browser supports the native JSON interface.
        47 * @const
        48 * @see http://caniuse.com/#search=JSON
        49 * @private {boolean}
        50 */
        51bot.json.SUPPORTS_NATIVE_JSON_ =
        52 // List WebKit first since every supported version supports
        53 // native JSON (and we can compile away large chunks of code for
        54 // individual fragments by setting the appropriate compiler flags).
        55 goog.userAgent.WEBKIT ||
        56 (goog.userAgent.GECKO && bot.userAgent.isEngineVersion(3.5)) ||
        57 (goog.userAgent.IE && bot.userAgent.isEngineVersion(8));
        58
        59
        60/**
        61 * Converts a JSON object to its string representation.
        62 * @param {*} jsonObj The input object.
        63 * @param {?(function(string, *): *)=} opt_replacer A replacer function called
        64 * for each (key, value) pair that determines how the value should be
        65 * serialized. By default, this just returns the value and allows default
        66 * serialization to kick in.
        67 * @return {string} A JSON string representation of the input object.
        68 */
        69bot.json.stringify = bot.json.NATIVE_JSON && bot.json.SUPPORTS_NATIVE_JSON_ ?
        70 JSON.stringify : goog.json.serialize;
        71
        72
        73/**
        74 * Parses a JSON string and returns the result.
        75 * @param {string} jsonStr The string to parse.
        76 * @return {*} The JSON object.
        77 * @throws {Error} If the input string is an invalid JSON string.
        78 */
        79bot.json.parse = bot.json.NATIVE_JSON && bot.json.SUPPORTS_NATIVE_JSON_ ?
        80 JSON.parse : goog.json.parse;
        \ No newline at end of file diff --git a/docs/source/lib/atoms/response.js.src.html b/docs/source/lib/atoms/response.js.src.html index 4a2bb7c..700e670 100644 --- a/docs/source/lib/atoms/response.js.src.html +++ b/docs/source/lib/atoms/response.js.src.html @@ -1 +1 @@ -response.js

        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
        20goog.provide('bot.response');
        21goog.provide('bot.response.ResponseObject');
        22
        23goog.require('bot.Error');
        24goog.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 */
        32bot.response.ResponseObject;
        33
        34
        35/**
        36 * @param {*} value The value to test.
        37 * @return {boolean} Whether the given value is a response object.
        38 */
        39bot.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 */
        49bot.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 */
        66bot.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 */
        92bot.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};
        \ No newline at end of file +response.js

        lib/atoms/response.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Utilities for working with WebDriver response objects.
        20 * @see: hhttps://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#responses
        21 */
        22
        23goog.provide('bot.response');
        24goog.provide('bot.response.ResponseObject');
        25
        26goog.require('bot.Error');
        27goog.require('bot.ErrorCode');
        28
        29
        30/**
        31 * Type definition for a response object, as defined by the JSON wire protocol.
        32 * @typedef {{status: bot.ErrorCode, value: (*|{message: string})}}
        33 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#responses
        34 */
        35bot.response.ResponseObject;
        36
        37
        38/**
        39 * @param {*} value The value to test.
        40 * @return {boolean} Whether the given value is a response object.
        41 */
        42bot.response.isResponseObject = function(value) {
        43 return goog.isObject(value) && goog.isNumber(value['status']);
        44};
        45
        46
        47/**
        48 * Creates a new success response object with the provided value.
        49 * @param {*} value The response value.
        50 * @return {!bot.response.ResponseObject} The new response object.
        51 */
        52bot.response.createResponse = function(value) {
        53 if (bot.response.isResponseObject(value)) {
        54 return /** @type {!bot.response.ResponseObject} */ (value);
        55 }
        56 return {
        57 'status': bot.ErrorCode.SUCCESS,
        58 'value': value
        59 };
        60};
        61
        62
        63/**
        64 * Converts an error value into its JSON representation as defined by the
        65 * WebDriver wire protocol.
        66 * @param {(bot.Error|Error|*)} error The error value to convert.
        67 * @return {!bot.response.ResponseObject} The new response object.
        68 */
        69bot.response.createErrorResponse = function(error) {
        70 if (bot.response.isResponseObject(error)) {
        71 return /** @type {!bot.response.ResponseObject} */ (error);
        72 }
        73
        74 var statusCode = error && goog.isNumber(error.code) ? error.code :
        75 bot.ErrorCode.UNKNOWN_ERROR;
        76 return {
        77 'status': /** @type {bot.ErrorCode} */ (statusCode),
        78 'value': {
        79 'message': (error && error.message || error) + ''
        80 }
        81 };
        82};
        83
        84
        85/**
        86 * Checks that a response object does not specify an error as defined by the
        87 * WebDriver wire protocol. If the response object defines an error, it will
        88 * be thrown. Otherwise, the response will be returned as is.
        89 * @param {!bot.response.ResponseObject} responseObj The response object to
        90 * check.
        91 * @return {!bot.response.ResponseObject} The checked response object.
        92 * @throws {bot.Error} If the response describes an error.
        93 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#failed-commands
        94 */
        95bot.response.checkResponse = function(responseObj) {
        96 var status = responseObj['status'];
        97 if (status == bot.ErrorCode.SUCCESS) {
        98 return responseObj;
        99 }
        100
        101 // If status is not defined, assume an unknown error.
        102 status = status || bot.ErrorCode.UNKNOWN_ERROR;
        103
        104 var value = responseObj['value'];
        105 if (!value || !goog.isObject(value)) {
        106 throw new bot.Error(status, value + '');
        107 }
        108
        109 throw new bot.Error(status, value['message'] + '');
        110};
        \ No newline at end of file diff --git a/docs/source/lib/atoms/userAgent.js.src.html b/docs/source/lib/atoms/userAgent.js.src.html index 9eed02c..ae1f199 100644 --- a/docs/source/lib/atoms/userAgent.js.src.html +++ b/docs/source/lib/atoms/userAgent.js.src.html @@ -1 +1 @@ -userAgent.js

        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 */
        20goog.provide('bot.userAgent');
        21
        22goog.require('goog.string');
        23goog.require('goog.userAgent');
        24goog.require('goog.userAgent.product');
        25goog.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 */
        42bot.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 */
        68bot.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 */
        86bot.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 */
        95bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_;
        96
        97
        98/**
        99 * Whether we are in a Firefox extension.
        100 *
        101 * @const
        102 * @type {boolean}
        103 */
        104bot.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 */
        150bot.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 */
        160bot.userAgent.MOBILE = bot.userAgent.IOS || goog.userAgent.product.ANDROID;
        161
        162
        163/**
        164 * Android Operating System Version.
        165 * @private {string}
        166 * @const
        167 */
        168bot.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 */
        184bot.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 */
        193bot.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 */
        201bot.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 */
        210bot.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 */
        218bot.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 */
        227bot.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 */
        236bot.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 */
        245bot.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 */
        254bot.userAgent.WINDOWS_PHONE = goog.userAgent.IE &&
        255 goog.userAgent.getUserAgentString().indexOf('IEMobile') != -1;
        \ No newline at end of file +userAgent.js

        lib/atoms/userAgent.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Similar to goog.userAgent.isVersion, but with support for
        20 * getting the version information when running in a firefox extension.
        21 */
        22goog.provide('bot.userAgent');
        23
        24goog.require('goog.string');
        25goog.require('goog.userAgent');
        26goog.require('goog.userAgent.product');
        27goog.require('goog.userAgent.product.isVersion');
        28
        29
        30/**
        31 * Whether the rendering engine version of the current browser is equal to or
        32 * greater than the given version. This implementation differs from
        33 * goog.userAgent.isVersion in the following ways:
        34 * <ol>
        35 * <li>in a Firefox extension, tests the engine version through the XUL version
        36 * comparator service, because no window.navigator object is available
        37 * <li>in IE, compares the given version to the current documentMode
        38 * </ol>
        39 *
        40 * @param {string|number} version The version number to check.
        41 * @return {boolean} Whether the browser engine version is the same or higher
        42 * than the given version.
        43 */
        44bot.userAgent.isEngineVersion = function(version) {
        45 if (bot.userAgent.FIREFOX_EXTENSION) {
        46 return bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_(version);
        47 } else if (goog.userAgent.IE) {
        48 return goog.string.compareVersions(
        49 /** @type {number} */ (goog.userAgent.DOCUMENT_MODE), version) >= 0;
        50 } else {
        51 return goog.userAgent.isVersionOrHigher(version);
        52 }
        53};
        54
        55
        56/**
        57 * Whether the product version of the current browser is equal to or greater
        58 * than the given version. This implementation differs from
        59 * goog.userAgent.product.isVersion in the following ways:
        60 * <ol>
        61 * <li>in a Firefox extension, tests the product version through the XUL version
        62 * comparator service, because no window.navigator object is available
        63 * <li>on Android, always compares to the version to the OS version
        64 * </ol>
        65 *
        66 * @param {string|number} version The version number to check.
        67 * @return {boolean} Whether the browser product version is the same or higher
        68 * than the given version.
        69 */
        70bot.userAgent.isProductVersion = function(version) {
        71 if (bot.userAgent.FIREFOX_EXTENSION) {
        72 return bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_(version);
        73 } else if (goog.userAgent.product.ANDROID) {
        74 return goog.string.compareVersions(
        75 bot.userAgent.ANDROID_VERSION_, version) >= 0;
        76 } else {
        77 return goog.userAgent.product.isVersion(version);
        78 }
        79};
        80
        81
        82/**
        83 * When we are in a Firefox extension, this is a function that accepts a version
        84 * and returns whether the version of Gecko we are on is the same or higher
        85 * than the given version. When we are not in a Firefox extension, this is null.
        86 * @private {(undefined|function((string|number)): boolean)}
        87 */
        88bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_;
        89
        90
        91/**
        92 * When we are in a Firefox extension, this is a function that accepts a version
        93 * and returns whether the version of Firefox we are on is the same or higher
        94 * than the given version. When we are not in a Firefox extension, this is null.
        95 * @private {(undefined|function((string|number)): boolean)}
        96 */
        97bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_;
        98
        99
        100/**
        101 * Whether we are in a Firefox extension.
        102 *
        103 * @const
        104 * @type {boolean}
        105 */
        106bot.userAgent.FIREFOX_EXTENSION = (function() {
        107 // False if this browser is not a Gecko browser.
        108 if (!goog.userAgent.GECKO) {
        109 return false;
        110 }
        111
        112 // False if this code isn't running in an extension.
        113 var Components = goog.global.Components;
        114 if (!Components) {
        115 return false;
        116 }
        117 try {
        118 if (!Components['classes']) {
        119 return false;
        120 }
        121 } catch (e) {
        122 return false;
        123 }
        124
        125 // Populate the version checker functions.
        126 var cc = Components['classes'];
        127 var ci = Components['interfaces'];
        128 var versionComparator = cc['@mozilla.org/xpcom/version-comparator;1'][
        129 'getService'](ci['nsIVersionComparator']);
        130 var appInfo = cc['@mozilla.org/xre/app-info;1']['getService'](
        131 ci['nsIXULAppInfo']);
        132 var geckoVersion = appInfo['platformVersion'];
        133 var firefoxVersion = appInfo['version'];
        134
        135 bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_ = function(version) {
        136 return versionComparator.compare(geckoVersion, '' + version) >= 0;
        137 };
        138 bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_ = function(version) {
        139 return versionComparator.compare(firefoxVersion, '' + version) >= 0;
        140 };
        141
        142 return true;
        143})();
        144
        145
        146/**
        147 * Whether we are on IOS.
        148 *
        149 * @const
        150 * @type {boolean}
        151 */
        152bot.userAgent.IOS = goog.userAgent.product.IPAD ||
        153 goog.userAgent.product.IPHONE;
        154
        155
        156/**
        157 * Whether we are on a mobile browser.
        158 *
        159 * @const
        160 * @type {boolean}
        161 */
        162bot.userAgent.MOBILE = bot.userAgent.IOS || goog.userAgent.product.ANDROID;
        163
        164
        165/**
        166 * Android Operating System Version.
        167 * @private {string}
        168 * @const
        169 */
        170bot.userAgent.ANDROID_VERSION_ = (function() {
        171 if (goog.userAgent.product.ANDROID) {
        172 var userAgentString = goog.userAgent.getUserAgentString();
        173 var match = /Android\s+([0-9\.]+)/.exec(userAgentString);
        174 return match ? match[1] : '0';
        175 } else {
        176 return '0';
        177 }
        178})();
        179
        180
        181/**
        182 * Whether the current document is IE in a documentMode older than 8.
        183 * @type {boolean}
        184 * @const
        185 */
        186bot.userAgent.IE_DOC_PRE8 = goog.userAgent.IE &&
        187 !goog.userAgent.isDocumentModeOrHigher(8);
        188
        189
        190/**
        191 * Whether the current document is IE in IE9 (or newer) standards mode.
        192 * @type {boolean}
        193 * @const
        194 */
        195bot.userAgent.IE_DOC_9 = goog.userAgent.isDocumentModeOrHigher(9);
        196
        197
        198/**
        199 * Whether the current document is IE in a documentMode older than 9.
        200 * @type {boolean}
        201 * @const
        202 */
        203bot.userAgent.IE_DOC_PRE9 = goog.userAgent.IE &&
        204 !goog.userAgent.isDocumentModeOrHigher(9);
        205
        206
        207/**
        208 * Whether the current document is IE in IE10 (or newer) standards mode.
        209 * @type {boolean}
        210 * @const
        211 */
        212bot.userAgent.IE_DOC_10 = goog.userAgent.isDocumentModeOrHigher(10);
        213
        214
        215/**
        216 * Whether the current document is IE in a documentMode older than 10.
        217 * @type {boolean}
        218 * @const
        219 */
        220bot.userAgent.IE_DOC_PRE10 = goog.userAgent.IE &&
        221 !goog.userAgent.isDocumentModeOrHigher(10);
        222
        223
        224/**
        225 * Whether the current browser is Android pre-gingerbread.
        226 * @type {boolean}
        227 * @const
        228 */
        229bot.userAgent.ANDROID_PRE_GINGERBREAD = goog.userAgent.product.ANDROID &&
        230 !bot.userAgent.isProductVersion(2.3);
        231
        232
        233/**
        234 * Whether the current browser is Android pre-icecreamsandwich
        235 * @type {boolean}
        236 * @const
        237 */
        238bot.userAgent.ANDROID_PRE_ICECREAMSANDWICH = goog.userAgent.product.ANDROID &&
        239 !bot.userAgent.isProductVersion(4);
        240
        241
        242/**
        243 * Whether the current browser is Safari 6.
        244 * @type {boolean}
        245 * @const
        246 */
        247bot.userAgent.SAFARI_6 = goog.userAgent.product.SAFARI &&
        248 bot.userAgent.isProductVersion(6);
        249
        250
        251/**
        252 * Whether the current browser is Windows Phone.
        253 * @type {boolean}
        254 * @const
        255 */
        256bot.userAgent.WINDOWS_PHONE = goog.userAgent.IE &&
        257 goog.userAgent.getUserAgentString().indexOf('IEMobile') != -1;
        \ No newline at end of file diff --git a/docs/source/lib/goog/array/array.js.src.html b/docs/source/lib/goog/array/array.js.src.html index aab8e53..a03714d 100644 --- a/docs/source/lib/goog/array/array.js.src.html +++ b/docs/source/lib/goog/array/array.js.src.html @@ -1 +1 @@ -array.js

        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
        21goog.provide('goog.array');
        22goog.provide('goog.array.ArrayLike');
        23
        24goog.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 */
        42goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);
        43
        44
        45/**
        46 * @typedef {Array|NodeList|Arguments|{length: number}}
        47 */
        48goog.array.ArrayLike;
        49
        50
        51/**
        52 * Returns the last element in an array without removing it.
        53 * @param {goog.array.ArrayLike} array The array.
        54 * @return {*} Last item in array.
        55 */
        56goog.array.peek = function(array) {
        57 return array[array.length - 1];
        58};
        59
        60
        61/**
        62 * Reference to the original {@code Array.prototype}.
        63 * @private
        64 */
        65goog.array.ARRAY_PROTOTYPE_ = Array.prototype;
        66
        67
        68// NOTE(arv): Since most of the array functions are generic it allows you to
        69// pass an array-like object. Strings have a length and are considered array-
        70// like. However, the 'in' operator does not work on strings so we cannot just
        71// use the array path even if the browser supports indexing into strings. We
        72// therefore end up splitting the string.
        73
        74
        75/**
        76 * Returns the index of the first element of an array with a specified
        77 * value, or -1 if the element is not present in the array.
        78 *
        79 * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}
        80 *
        81 * @param {goog.array.ArrayLike} arr The array to be searched.
        82 * @param {*} obj The object for which we are searching.
        83 * @param {number=} opt_fromIndex The index at which to start the search. If
        84 * omitted the search starts at index 0.
        85 * @return {number} The index of the first matching array element.
        86 */
        87goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
        88 goog.array.ARRAY_PROTOTYPE_.indexOf ?
        89 function(arr, obj, opt_fromIndex) {
        90 goog.asserts.assert(arr.length != null);
        91
        92 return goog.array.ARRAY_PROTOTYPE_.indexOf.call(arr, obj, opt_fromIndex);
        93 } :
        94 function(arr, obj, opt_fromIndex) {
        95 var fromIndex = opt_fromIndex == null ?
        96 0 : (opt_fromIndex < 0 ?
        97 Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex);
        98
        99 if (goog.isString(arr)) {
        100 // Array.prototype.indexOf uses === so only strings should be found.
        101 if (!goog.isString(obj) || obj.length != 1) {
        102 return -1;
        103 }
        104 return arr.indexOf(obj, fromIndex);
        105 }
        106
        107 for (var i = fromIndex; i < arr.length; i++) {
        108 if (i in arr && arr[i] === obj)
        109 return i;
        110 }
        111 return -1;
        112 };
        113
        114
        115/**
        116 * Returns the index of the last element of an array with a specified value, or
        117 * -1 if the element is not present in the array.
        118 *
        119 * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}
        120 *
        121 * @param {goog.array.ArrayLike} arr The array to be searched.
        122 * @param {*} obj The object for which we are searching.
        123 * @param {?number=} opt_fromIndex The index at which to start the search. If
        124 * omitted the search starts at the end of the array.
        125 * @return {number} The index of the last matching array element.
        126 */
        127goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
        128 goog.array.ARRAY_PROTOTYPE_.lastIndexOf ?
        129 function(arr, obj, opt_fromIndex) {
        130 goog.asserts.assert(arr.length != null);
        131
        132 // Firefox treats undefined and null as 0 in the fromIndex argument which
        133 // leads it to always return -1
        134 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
        135 return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(arr, obj, fromIndex);
        136 } :
        137 function(arr, obj, opt_fromIndex) {
        138 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
        139
        140 if (fromIndex < 0) {
        141 fromIndex = Math.max(0, arr.length + fromIndex);
        142 }
        143
        144 if (goog.isString(arr)) {
        145 // Array.prototype.lastIndexOf uses === so only strings should be found.
        146 if (!goog.isString(obj) || obj.length != 1) {
        147 return -1;
        148 }
        149 return arr.lastIndexOf(obj, fromIndex);
        150 }
        151
        152 for (var i = fromIndex; i >= 0; i--) {
        153 if (i in arr && arr[i] === obj)
        154 return i;
        155 }
        156 return -1;
        157 };
        158
        159
        160/**
        161 * Calls a function for each element in an array. Skips holes in the array.
        162 * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}
        163 *
        164 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over
        165 * which to iterate.
        166 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
        167 * element. This function takes 3 arguments (the element, the index and the
        168 * array). The return value is ignored.
        169 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
        170 * @template T,S
        171 */
        172goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES &&
        173 goog.array.ARRAY_PROTOTYPE_.forEach ?
        174 function(arr, f, opt_obj) {
        175 goog.asserts.assert(arr.length != null);
        176
        177 goog.array.ARRAY_PROTOTYPE_.forEach.call(arr, f, opt_obj);
        178 } :
        179 function(arr, f, opt_obj) {
        180 var l = arr.length; // must be fixed during loop... see docs
        181 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        182 for (var i = 0; i < l; i++) {
        183 if (i in arr2) {
        184 f.call(opt_obj, arr2[i], i, arr);
        185 }
        186 }
        187 };
        188
        189
        190/**
        191 * Calls a function for each element in an array, starting from the last
        192 * element rather than the first.
        193 *
        194 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        195 * like object over which to iterate.
        196 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
        197 * element. This function
        198 * takes 3 arguments (the element, the index and the array). The return
        199 * value is ignored.
        200 * @param {S=} opt_obj The object to be used as the value of 'this'
        201 * within f.
        202 * @template T,S
        203 */
        204goog.array.forEachRight = 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 = l - 1; i >= 0; --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, and if the function returns
        217 * true adds the element to a new array.
        218 *
        219 * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}
        220 *
        221 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        222 * like object over which to iterate.
        223 * @param {?function(this:S, T, number, ?):boolean} f The function to call for
        224 * every element. This function
        225 * takes 3 arguments (the element, the index and the array) and must
        226 * return a Boolean. If the return value is true the element is added to the
        227 * result array. If it is false the element is not included.
        228 * @param {S=} opt_obj The object to be used as the value of 'this'
        229 * within f.
        230 * @return {!Array} a new array in which only elements that passed the test are
        231 * present.
        232 * @template T,S
        233 */
        234goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES &&
        235 goog.array.ARRAY_PROTOTYPE_.filter ?
        236 function(arr, f, opt_obj) {
        237 goog.asserts.assert(arr.length != null);
        238
        239 return goog.array.ARRAY_PROTOTYPE_.filter.call(arr, f, opt_obj);
        240 } :
        241 function(arr, f, opt_obj) {
        242 var l = arr.length; // must be fixed during loop... see docs
        243 var res = [];
        244 var resLength = 0;
        245 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        246 for (var i = 0; i < l; i++) {
        247 if (i in arr2) {
        248 var val = arr2[i]; // in case f mutates arr2
        249 if (f.call(opt_obj, val, i, arr)) {
        250 res[resLength++] = val;
        251 }
        252 }
        253 }
        254 return res;
        255 };
        256
        257
        258/**
        259 * Calls a function for each element in an array and inserts the result into a
        260 * new array.
        261 *
        262 * See {@link http://tinyurl.com/developer-mozilla-org-array-map}
        263 *
        264 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        265 * like object over which to iterate.
        266 * @param {?function(this:S, T, number, ?):?} f The function to call for every
        267 * element. This function
        268 * takes 3 arguments (the element, the index and the array) and should
        269 * return something. The result will be inserted into a new array.
        270 * @param {S=} opt_obj The object to be used as the value of 'this'
        271 * within f.
        272 * @return {!Array} a new array with the results from f.
        273 * @template T,S
        274 */
        275goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES &&
        276 goog.array.ARRAY_PROTOTYPE_.map ?
        277 function(arr, f, opt_obj) {
        278 goog.asserts.assert(arr.length != null);
        279
        280 return goog.array.ARRAY_PROTOTYPE_.map.call(arr, f, opt_obj);
        281 } :
        282 function(arr, f, opt_obj) {
        283 var l = arr.length; // must be fixed during loop... see docs
        284 var res = new Array(l);
        285 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        286 for (var i = 0; i < l; i++) {
        287 if (i in arr2) {
        288 res[i] = f.call(opt_obj, arr2[i], i, arr);
        289 }
        290 }
        291 return res;
        292 };
        293
        294
        295/**
        296 * Passes every element of an array into a function and accumulates the result.
        297 *
        298 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}
        299 *
        300 * For example:
        301 * var a = [1, 2, 3, 4];
        302 * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);
        303 * returns 10
        304 *
        305 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        306 * like object over which to iterate.
        307 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for
        308 * every element. This function
        309 * takes 4 arguments (the function's previous result or the initial value,
        310 * the value of the current array element, the current array index, and the
        311 * array itself)
        312 * function(previousValue, currentValue, index, array).
        313 * @param {?} val The initial value to pass into the function on the first call.
        314 * @param {S=} opt_obj The object to be used as the value of 'this'
        315 * within f.
        316 * @return {R} Result of evaluating f repeatedly across the values of the array.
        317 * @template T,S,R
        318 */
        319goog.array.reduce = function(arr, f, val, opt_obj) {
        320 if (arr.reduce) {
        321 if (opt_obj) {
        322 return arr.reduce(goog.bind(f, opt_obj), val);
        323 } else {
        324 return arr.reduce(f, val);
        325 }
        326 }
        327 var rval = val;
        328 goog.array.forEach(arr, function(val, index) {
        329 rval = f.call(opt_obj, rval, val, index, arr);
        330 });
        331 return rval;
        332};
        333
        334
        335/**
        336 * Passes every element of an array into a function and accumulates the result,
        337 * starting from the last element and working towards the first.
        338 *
        339 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}
        340 *
        341 * For example:
        342 * var a = ['a', 'b', 'c'];
        343 * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');
        344 * returns 'cba'
        345 *
        346 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        347 * like object over which to iterate.
        348 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for
        349 * every element. This function
        350 * takes 4 arguments (the function's previous result or the initial value,
        351 * the value of the current array element, the current array index, and the
        352 * array itself)
        353 * function(previousValue, currentValue, index, array).
        354 * @param {?} val The initial value to pass into the function on the first call.
        355 * @param {S=} opt_obj The object to be used as the value of 'this'
        356 * within f.
        357 * @return {R} Object returned as a result of evaluating f repeatedly across the
        358 * values of the array.
        359 * @template T,S,R
        360 */
        361goog.array.reduceRight = function(arr, f, val, opt_obj) {
        362 if (arr.reduceRight) {
        363 if (opt_obj) {
        364 return arr.reduceRight(goog.bind(f, opt_obj), val);
        365 } else {
        366 return arr.reduceRight(f, val);
        367 }
        368 }
        369 var rval = val;
        370 goog.array.forEachRight(arr, function(val, index) {
        371 rval = f.call(opt_obj, rval, val, index, arr);
        372 });
        373 return rval;
        374};
        375
        376
        377/**
        378 * Calls f for each element of an array. If any call returns true, some()
        379 * returns true (without checking the remaining elements). If all calls
        380 * return false, some() returns false.
        381 *
        382 * See {@link http://tinyurl.com/developer-mozilla-org-array-some}
        383 *
        384 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        385 * like object over which to iterate.
        386 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
        387 * for every element. This function takes 3 arguments (the element, the
        388 * index and the array) and should return a boolean.
        389 * @param {S=} opt_obj The object to be used as the value of 'this'
        390 * within f.
        391 * @return {boolean} true if any element passes the test.
        392 * @template T,S
        393 */
        394goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES &&
        395 goog.array.ARRAY_PROTOTYPE_.some ?
        396 function(arr, f, opt_obj) {
        397 goog.asserts.assert(arr.length != null);
        398
        399 return goog.array.ARRAY_PROTOTYPE_.some.call(arr, f, opt_obj);
        400 } :
        401 function(arr, f, opt_obj) {
        402 var l = arr.length; // must be fixed during loop... see docs
        403 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        404 for (var i = 0; i < l; i++) {
        405 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
        406 return true;
        407 }
        408 }
        409 return false;
        410 };
        411
        412
        413/**
        414 * Call f for each element of an array. If all calls return true, every()
        415 * returns true. If any call returns false, every() returns false and
        416 * does not continue to check the remaining elements.
        417 *
        418 * See {@link http://tinyurl.com/developer-mozilla-org-array-every}
        419 *
        420 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        421 * like object over which to iterate.
        422 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
        423 * for every element. This function takes 3 arguments (the element, the
        424 * index and the array) and should return a boolean.
        425 * @param {S=} opt_obj The object to be used as the value of 'this'
        426 * within f.
        427 * @return {boolean} false if any element fails the test.
        428 * @template T,S
        429 */
        430goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES &&
        431 goog.array.ARRAY_PROTOTYPE_.every ?
        432 function(arr, f, opt_obj) {
        433 goog.asserts.assert(arr.length != null);
        434
        435 return goog.array.ARRAY_PROTOTYPE_.every.call(arr, f, opt_obj);
        436 } :
        437 function(arr, f, opt_obj) {
        438 var l = arr.length; // must be fixed during loop... see docs
        439 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        440 for (var i = 0; i < l; i++) {
        441 if (i in arr2 && !f.call(opt_obj, arr2[i], i, arr)) {
        442 return false;
        443 }
        444 }
        445 return true;
        446 };
        447
        448
        449/**
        450 * Counts the array elements that fulfill the predicate, i.e. for which the
        451 * callback function returns true. Skips holes in the array.
        452 *
        453 * @param {!(Array.<T>|goog.array.ArrayLike)} arr Array or array like object
        454 * over which to iterate.
        455 * @param {function(this: S, T, number, ?): boolean} f The function to call for
        456 * every element. Takes 3 arguments (the element, the index and the array).
        457 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
        458 * @return {number} The number of the matching elements.
        459 * @template T,S
        460 */
        461goog.array.count = function(arr, f, opt_obj) {
        462 var count = 0;
        463 goog.array.forEach(arr, function(element, index, arr) {
        464 if (f.call(opt_obj, element, index, arr)) {
        465 ++count;
        466 }
        467 }, opt_obj);
        468 return count;
        469};
        470
        471
        472/**
        473 * Search an array for the first element that satisfies a given condition and
        474 * return that element.
        475 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        476 * like object over which to iterate.
        477 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        478 * for every element. This function takes 3 arguments (the element, the
        479 * index and the array) and should return a boolean.
        480 * @param {S=} opt_obj An optional "this" context for the function.
        481 * @return {T} The first array element that passes the test, or null if no
        482 * element is found.
        483 * @template T,S
        484 */
        485goog.array.find = function(arr, f, opt_obj) {
        486 var i = goog.array.findIndex(arr, f, opt_obj);
        487 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
        488};
        489
        490
        491/**
        492 * Search an array for the first element that satisfies a given condition and
        493 * return its index.
        494 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        495 * like object over which to iterate.
        496 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
        497 * every element. This function
        498 * takes 3 arguments (the element, the index and the array) and should
        499 * return a boolean.
        500 * @param {S=} opt_obj An optional "this" context for the function.
        501 * @return {number} The index of the first array element that passes the test,
        502 * or -1 if no element is found.
        503 * @template T,S
        504 */
        505goog.array.findIndex = function(arr, f, opt_obj) {
        506 var l = arr.length; // must be fixed during loop... see docs
        507 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        508 for (var i = 0; i < l; i++) {
        509 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
        510 return i;
        511 }
        512 }
        513 return -1;
        514};
        515
        516
        517/**
        518 * Search an array (in reverse order) for the last element that satisfies a
        519 * given condition and return that element.
        520 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        521 * like object over which to iterate.
        522 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        523 * for every element. This function
        524 * takes 3 arguments (the element, the index and the array) and should
        525 * return a boolean.
        526 * @param {S=} opt_obj An optional "this" context for the function.
        527 * @return {T} The last array element that passes the test, or null if no
        528 * element is found.
        529 * @template T,S
        530 */
        531goog.array.findRight = function(arr, f, opt_obj) {
        532 var i = goog.array.findIndexRight(arr, f, opt_obj);
        533 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
        534};
        535
        536
        537/**
        538 * Search an array (in reverse order) for the last element that satisfies a
        539 * given condition and return its index.
        540 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        541 * like object over which to iterate.
        542 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        543 * for every element. This function
        544 * takes 3 arguments (the element, the index and the array) and should
        545 * return a boolean.
        546 * @param {Object=} opt_obj An optional "this" context for the function.
        547 * @return {number} The index of the last array element that passes the test,
        548 * or -1 if no element is found.
        549 * @template T,S
        550 */
        551goog.array.findIndexRight = function(arr, f, opt_obj) {
        552 var l = arr.length; // must be fixed during loop... see docs
        553 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        554 for (var i = l - 1; i >= 0; i--) {
        555 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
        556 return i;
        557 }
        558 }
        559 return -1;
        560};
        561
        562
        563/**
        564 * Whether the array contains the given object.
        565 * @param {goog.array.ArrayLike} arr The array to test for the presence of the
        566 * element.
        567 * @param {*} obj The object for which to test.
        568 * @return {boolean} true if obj is present.
        569 */
        570goog.array.contains = function(arr, obj) {
        571 return goog.array.indexOf(arr, obj) >= 0;
        572};
        573
        574
        575/**
        576 * Whether the array is empty.
        577 * @param {goog.array.ArrayLike} arr The array to test.
        578 * @return {boolean} true if empty.
        579 */
        580goog.array.isEmpty = function(arr) {
        581 return arr.length == 0;
        582};
        583
        584
        585/**
        586 * Clears the array.
        587 * @param {goog.array.ArrayLike} arr Array or array like object to clear.
        588 */
        589goog.array.clear = function(arr) {
        590 // For non real arrays we don't have the magic length so we delete the
        591 // indices.
        592 if (!goog.isArray(arr)) {
        593 for (var i = arr.length - 1; i >= 0; i--) {
        594 delete arr[i];
        595 }
        596 }
        597 arr.length = 0;
        598};
        599
        600
        601/**
        602 * Pushes an item into an array, if it's not already in the array.
        603 * @param {Array.<T>} arr Array into which to insert the item.
        604 * @param {T} obj Value to add.
        605 * @template T
        606 */
        607goog.array.insert = function(arr, obj) {
        608 if (!goog.array.contains(arr, obj)) {
        609 arr.push(obj);
        610 }
        611};
        612
        613
        614/**
        615 * Inserts an object at the given index of the array.
        616 * @param {goog.array.ArrayLike} arr The array to modify.
        617 * @param {*} obj The object to insert.
        618 * @param {number=} opt_i The index at which to insert the object. If omitted,
        619 * treated as 0. A negative index is counted from the end of the array.
        620 */
        621goog.array.insertAt = function(arr, obj, opt_i) {
        622 goog.array.splice(arr, opt_i, 0, obj);
        623};
        624
        625
        626/**
        627 * Inserts at the given index of the array, all elements of another array.
        628 * @param {goog.array.ArrayLike} arr The array to modify.
        629 * @param {goog.array.ArrayLike} elementsToAdd The array of elements to add.
        630 * @param {number=} opt_i The index at which to insert the object. If omitted,
        631 * treated as 0. A negative index is counted from the end of the array.
        632 */
        633goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {
        634 goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd);
        635};
        636
        637
        638/**
        639 * Inserts an object into an array before a specified object.
        640 * @param {Array.<T>} arr The array to modify.
        641 * @param {T} obj The object to insert.
        642 * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2
        643 * is omitted or not found, obj is inserted at the end of the array.
        644 * @template T
        645 */
        646goog.array.insertBefore = function(arr, obj, opt_obj2) {
        647 var i;
        648 if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {
        649 arr.push(obj);
        650 } else {
        651 goog.array.insertAt(arr, obj, i);
        652 }
        653};
        654
        655
        656/**
        657 * Removes the first occurrence of a particular value from an array.
        658 * @param {goog.array.ArrayLike} arr Array from which to remove value.
        659 * @param {*} obj Object to remove.
        660 * @return {boolean} True if an element was removed.
        661 */
        662goog.array.remove = function(arr, obj) {
        663 var i = goog.array.indexOf(arr, obj);
        664 var rv;
        665 if ((rv = i >= 0)) {
        666 goog.array.removeAt(arr, i);
        667 }
        668 return rv;
        669};
        670
        671
        672/**
        673 * Removes from an array the element at index i
        674 * @param {goog.array.ArrayLike} arr Array or array like object from which to
        675 * remove value.
        676 * @param {number} i The index to remove.
        677 * @return {boolean} True if an element was removed.
        678 */
        679goog.array.removeAt = function(arr, i) {
        680 goog.asserts.assert(arr.length != null);
        681
        682 // use generic form of splice
        683 // splice returns the removed items and if successful the length of that
        684 // will be 1
        685 return goog.array.ARRAY_PROTOTYPE_.splice.call(arr, i, 1).length == 1;
        686};
        687
        688
        689/**
        690 * Removes the first value that satisfies the given condition.
        691 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array
        692 * like object over which to iterate.
        693 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        694 * for every element. This function
        695 * takes 3 arguments (the element, the index and the array) and should
        696 * return a boolean.
        697 * @param {S=} opt_obj An optional "this" context for the function.
        698 * @return {boolean} True if an element was removed.
        699 * @template T,S
        700 */
        701goog.array.removeIf = function(arr, f, opt_obj) {
        702 var i = goog.array.findIndex(arr, f, opt_obj);
        703 if (i >= 0) {
        704 goog.array.removeAt(arr, i);
        705 return true;
        706 }
        707 return false;
        708};
        709
        710
        711/**
        712 * Returns a new array that is the result of joining the arguments. If arrays
        713 * are passed then their items are added, however, if non-arrays are passed they
        714 * will be added to the return array as is.
        715 *
        716 * Note that ArrayLike objects will be added as is, rather than having their
        717 * items added.
        718 *
        719 * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]
        720 * goog.array.concat(0, [1, 2]) -> [0, 1, 2]
        721 * goog.array.concat([1, 2], null) -> [1, 2, null]
        722 *
        723 * There is bug in all current versions of IE (6, 7 and 8) where arrays created
        724 * in an iframe become corrupted soon (not immediately) after the iframe is
        725 * destroyed. This is common if loading data via goog.net.IframeIo, for example.
        726 * This corruption only affects the concat method which will start throwing
        727 * Catastrophic Errors (#-2147418113).
        728 *
        729 * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.
        730 *
        731 * Internally goog.array should use this, so that all methods will continue to
        732 * work on these broken array objects.
        733 *
        734 * @param {...*} var_args Items to concatenate. Arrays will have each item
        735 * added, while primitives and objects will be added as is.
        736 * @return {!Array} The new resultant array.
        737 */
        738goog.array.concat = function(var_args) {
        739 return goog.array.ARRAY_PROTOTYPE_.concat.apply(
        740 goog.array.ARRAY_PROTOTYPE_, arguments);
        741};
        742
        743
        744/**
        745 * Converts an object to an array.
        746 * @param {goog.array.ArrayLike} object The object to convert to an array.
        747 * @return {!Array} The object converted into an array. If object has a
        748 * length property, every property indexed with a non-negative number
        749 * less than length will be included in the result. If object does not
        750 * have a length property, an empty array will be returned.
        751 */
        752goog.array.toArray = function(object) {
        753 var length = object.length;
        754
        755 // If length is not a number the following it false. This case is kept for
        756 // backwards compatibility since there are callers that pass objects that are
        757 // not array like.
        758 if (length > 0) {
        759 var rv = new Array(length);
        760 for (var i = 0; i < length; i++) {
        761 rv[i] = object[i];
        762 }
        763 return rv;
        764 }
        765 return [];
        766};
        767
        768
        769/**
        770 * Does a shallow copy of an array.
        771 * @param {goog.array.ArrayLike} arr Array or array-like object to clone.
        772 * @return {!Array} Clone of the input array.
        773 */
        774goog.array.clone = goog.array.toArray;
        775
        776
        777/**
        778 * Extends an array with another array, element, or "array like" object.
        779 * This function operates 'in-place', it does not create a new Array.
        780 *
        781 * Example:
        782 * var a = [];
        783 * goog.array.extend(a, [0, 1]);
        784 * a; // [0, 1]
        785 * goog.array.extend(a, 2);
        786 * a; // [0, 1, 2]
        787 *
        788 * @param {Array} arr1 The array to modify.
        789 * @param {...*} var_args The elements or arrays of elements to add to arr1.
        790 */
        791goog.array.extend = function(arr1, var_args) {
        792 for (var i = 1; i < arguments.length; i++) {
        793 var arr2 = arguments[i];
        794 // If we have an Array or an Arguments object we can just call push
        795 // directly.
        796 var isArrayLike;
        797 if (goog.isArray(arr2) ||
        798 // Detect Arguments. ES5 says that the [[Class]] of an Arguments object
        799 // is "Arguments" but only V8 and JSC/Safari gets this right. We instead
        800 // detect Arguments by checking for array like and presence of "callee".
        801 (isArrayLike = goog.isArrayLike(arr2)) &&
        802 // The getter for callee throws an exception in strict mode
        803 // according to section 10.6 in ES5 so check for presence instead.
        804 Object.prototype.hasOwnProperty.call(arr2, 'callee')) {
        805 arr1.push.apply(arr1, arr2);
        806 } else if (isArrayLike) {
        807 // Otherwise loop over arr2 to prevent copying the object.
        808 var len1 = arr1.length;
        809 var len2 = arr2.length;
        810 for (var j = 0; j < len2; j++) {
        811 arr1[len1 + j] = arr2[j];
        812 }
        813 } else {
        814 arr1.push(arr2);
        815 }
        816 }
        817};
        818
        819
        820/**
        821 * Adds or removes elements from an array. This is a generic version of Array
        822 * splice. This means that it might work on other objects similar to arrays,
        823 * such as the arguments object.
        824 *
        825 * @param {goog.array.ArrayLike} arr The array to modify.
        826 * @param {number|undefined} index The index at which to start changing the
        827 * array. If not defined, treated as 0.
        828 * @param {number} howMany How many elements to remove (0 means no removal. A
        829 * value below 0 is treated as zero and so is any other non number. Numbers
        830 * are floored).
        831 * @param {...*} var_args Optional, additional elements to insert into the
        832 * array.
        833 * @return {!Array} the removed elements.
        834 */
        835goog.array.splice = function(arr, index, howMany, var_args) {
        836 goog.asserts.assert(arr.length != null);
        837
        838 return goog.array.ARRAY_PROTOTYPE_.splice.apply(
        839 arr, goog.array.slice(arguments, 1));
        840};
        841
        842
        843/**
        844 * Returns a new array from a segment of an array. This is a generic version of
        845 * Array slice. This means that it might work on other objects similar to
        846 * arrays, such as the arguments object.
        847 *
        848 * @param {Array.<T>|goog.array.ArrayLike} arr The array from
        849 * which to copy a segment.
        850 * @param {number} start The index of the first element to copy.
        851 * @param {number=} opt_end The index after the last element to copy.
        852 * @return {!Array.<T>} A new array containing the specified segment of the
        853 * original array.
        854 * @template T
        855 */
        856goog.array.slice = function(arr, start, opt_end) {
        857 goog.asserts.assert(arr.length != null);
        858
        859 // passing 1 arg to slice is not the same as passing 2 where the second is
        860 // null or undefined (in that case the second argument is treated as 0).
        861 // we could use slice on the arguments object and then use apply instead of
        862 // testing the length
        863 if (arguments.length <= 2) {
        864 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start);
        865 } else {
        866 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end);
        867 }
        868};
        869
        870
        871/**
        872 * Removes all duplicates from an array (retaining only the first
        873 * occurrence of each array element). This function modifies the
        874 * array in place and doesn't change the order of the non-duplicate items.
        875 *
        876 * For objects, duplicates are identified as having the same unique ID as
        877 * defined by {@link goog.getUid}.
        878 *
        879 * Runtime: N,
        880 * Worstcase space: 2N (no dupes)
        881 *
        882 * @param {goog.array.ArrayLike} arr The array from which to remove duplicates.
        883 * @param {Array=} opt_rv An optional array in which to return the results,
        884 * instead of performing the removal inplace. If specified, the original
        885 * array will remain unchanged.
        886 */
        887goog.array.removeDuplicates = function(arr, opt_rv) {
        888 var returnArray = opt_rv || arr;
        889
        890 var seen = {}, cursorInsert = 0, cursorRead = 0;
        891 while (cursorRead < arr.length) {
        892 var current = arr[cursorRead++];
        893
        894 // Prefix each type with a single character representing the type to
        895 // prevent conflicting keys (e.g. true and 'true').
        896 var key = goog.isObject(current) ?
        897 'o' + goog.getUid(current) :
        898 (typeof current).charAt(0) + current;
        899
        900 if (!Object.prototype.hasOwnProperty.call(seen, key)) {
        901 seen[key] = true;
        902 returnArray[cursorInsert++] = current;
        903 }
        904 }
        905 returnArray.length = cursorInsert;
        906};
        907
        908
        909/**
        910 * Searches the specified array for the specified target using the binary
        911 * search algorithm. If no opt_compareFn is specified, elements are compared
        912 * using <code>goog.array.defaultCompare</code>, which compares the elements
        913 * using the built in < and > operators. This will produce the expected
        914 * behavior for homogeneous arrays of String(s) and Number(s). The array
        915 * specified <b>must</b> be sorted in ascending order (as defined by the
        916 * comparison function). If the array is not sorted, results are undefined.
        917 * If the array contains multiple instances of the specified target value, any
        918 * of these instances may be found.
        919 *
        920 * Runtime: O(log n)
        921 *
        922 * @param {goog.array.ArrayLike} arr The array to be searched.
        923 * @param {*} target The sought value.
        924 * @param {Function=} opt_compareFn Optional comparison function by which the
        925 * array is ordered. Should take 2 arguments to compare, and return a
        926 * negative number, zero, or a positive number depending on whether the
        927 * first argument is less than, equal to, or greater than the second.
        928 * @return {number} Lowest index of the target value if found, otherwise
        929 * (-(insertion point) - 1). The insertion point is where the value should
        930 * be inserted into arr to preserve the sorted property. Return value >= 0
        931 * iff target is found.
        932 */
        933goog.array.binarySearch = function(arr, target, opt_compareFn) {
        934 return goog.array.binarySearch_(arr,
        935 opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */,
        936 target);
        937};
        938
        939
        940/**
        941 * Selects an index in the specified array using the binary search algorithm.
        942 * The evaluator receives an element and determines whether the desired index
        943 * is before, at, or after it. The evaluator must be consistent (formally,
        944 * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)
        945 * must be monotonically non-increasing).
        946 *
        947 * Runtime: O(log n)
        948 *
        949 * @param {goog.array.ArrayLike} arr The array to be searched.
        950 * @param {Function} evaluator Evaluator function that receives 3 arguments
        951 * (the element, the index and the array). Should return a negative number,
        952 * zero, or a positive number depending on whether the desired index is
        953 * before, at, or after the element passed to it.
        954 * @param {Object=} opt_obj The object to be used as the value of 'this'
        955 * within evaluator.
        956 * @return {number} Index of the leftmost element matched by the evaluator, if
        957 * such exists; otherwise (-(insertion point) - 1). The insertion point is
        958 * the index of the first element for which the evaluator returns negative,
        959 * or arr.length if no such element exists. The return value is non-negative
        960 * iff a match is found.
        961 */
        962goog.array.binarySelect = function(arr, evaluator, opt_obj) {
        963 return goog.array.binarySearch_(arr, evaluator, true /* isEvaluator */,
        964 undefined /* opt_target */, opt_obj);
        965};
        966
        967
        968/**
        969 * Implementation of a binary search algorithm which knows how to use both
        970 * comparison functions and evaluators. If an evaluator is provided, will call
        971 * the evaluator with the given optional data object, conforming to the
        972 * interface defined in binarySelect. Otherwise, if a comparison function is
        973 * provided, will call the comparison function against the given data object.
        974 *
        975 * This implementation purposefully does not use goog.bind or goog.partial for
        976 * performance reasons.
        977 *
        978 * Runtime: O(log n)
        979 *
        980 * @param {goog.array.ArrayLike} arr The array to be searched.
        981 * @param {Function} compareFn Either an evaluator or a comparison function,
        982 * as defined by binarySearch and binarySelect above.
        983 * @param {boolean} isEvaluator Whether the function is an evaluator or a
        984 * comparison function.
        985 * @param {*=} opt_target If the function is a comparison function, then this is
        986 * the target to binary search for.
        987 * @param {Object=} opt_selfObj If the function is an evaluator, this is an
        988 * optional this object for the evaluator.
        989 * @return {number} Lowest index of the target value if found, otherwise
        990 * (-(insertion point) - 1). The insertion point is where the value should
        991 * be inserted into arr to preserve the sorted property. Return value >= 0
        992 * iff target is found.
        993 * @private
        994 */
        995goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target,
        996 opt_selfObj) {
        997 var left = 0; // inclusive
        998 var right = arr.length; // exclusive
        999 var found;
        1000 while (left < right) {
        1001 var middle = (left + right) >> 1;
        1002 var compareResult;
        1003 if (isEvaluator) {
        1004 compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);
        1005 } else {
        1006 compareResult = compareFn(opt_target, arr[middle]);
        1007 }
        1008 if (compareResult > 0) {
        1009 left = middle + 1;
        1010 } else {
        1011 right = middle;
        1012 // We are looking for the lowest index so we can't return immediately.
        1013 found = !compareResult;
        1014 }
        1015 }
        1016 // left is the index if found, or the insertion point otherwise.
        1017 // ~left is a shorthand for -left - 1.
        1018 return found ? left : ~left;
        1019};
        1020
        1021
        1022/**
        1023 * Sorts the specified array into ascending order. If no opt_compareFn is
        1024 * specified, elements are compared using
        1025 * <code>goog.array.defaultCompare</code>, which compares the elements using
        1026 * the built in < and > operators. This will produce the expected behavior
        1027 * for homogeneous arrays of String(s) and Number(s), unlike the native sort,
        1028 * but will give unpredictable results for heterogenous lists of strings and
        1029 * numbers with different numbers of digits.
        1030 *
        1031 * This sort is not guaranteed to be stable.
        1032 *
        1033 * Runtime: Same as <code>Array.prototype.sort</code>
        1034 *
        1035 * @param {Array.<T>} arr The array to be sorted.
        1036 * @param {?function(T,T):number=} opt_compareFn Optional comparison
        1037 * function by which the
        1038 * array is to be ordered. Should take 2 arguments to compare, and return a
        1039 * negative number, zero, or a positive number depending on whether the
        1040 * first argument is less than, equal to, or greater than the second.
        1041 * @template T
        1042 */
        1043goog.array.sort = function(arr, opt_compareFn) {
        1044 // TODO(arv): Update type annotation since null is not accepted.
        1045 goog.asserts.assert(arr.length != null);
        1046
        1047 goog.array.ARRAY_PROTOTYPE_.sort.call(
        1048 arr, opt_compareFn || goog.array.defaultCompare);
        1049};
        1050
        1051
        1052/**
        1053 * Sorts the specified array into ascending order in a stable way. If no
        1054 * opt_compareFn is specified, elements are compared using
        1055 * <code>goog.array.defaultCompare</code>, which compares the elements using
        1056 * the built in < and > operators. This will produce the expected behavior
        1057 * for homogeneous arrays of String(s) and Number(s).
        1058 *
        1059 * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional
        1060 * O(n) overhead of copying the array twice.
        1061 *
        1062 * @param {Array.<T>} arr The array to be sorted.
        1063 * @param {?function(T, T): number=} opt_compareFn Optional comparison function
        1064 * by which the array is to be ordered. Should take 2 arguments to compare,
        1065 * and return a negative number, zero, or a positive number depending on
        1066 * whether the first argument is less than, equal to, or greater than the
        1067 * second.
        1068 * @template T
        1069 */
        1070goog.array.stableSort = function(arr, opt_compareFn) {
        1071 for (var i = 0; i < arr.length; i++) {
        1072 arr[i] = {index: i, value: arr[i]};
        1073 }
        1074 var valueCompareFn = opt_compareFn || goog.array.defaultCompare;
        1075 function stableCompareFn(obj1, obj2) {
        1076 return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;
        1077 };
        1078 goog.array.sort(arr, stableCompareFn);
        1079 for (var i = 0; i < arr.length; i++) {
        1080 arr[i] = arr[i].value;
        1081 }
        1082};
        1083
        1084
        1085/**
        1086 * Sorts an array of objects by the specified object key and compare
        1087 * function. If no compare function is provided, the key values are
        1088 * compared in ascending order using <code>goog.array.defaultCompare</code>.
        1089 * This won't work for keys that get renamed by the compiler. So use
        1090 * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
        1091 * @param {Array.<Object>} arr An array of objects to sort.
        1092 * @param {string} key The object key to sort by.
        1093 * @param {Function=} opt_compareFn The function to use to compare key
        1094 * values.
        1095 */
        1096goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {
        1097 var compare = opt_compareFn || goog.array.defaultCompare;
        1098 goog.array.sort(arr, function(a, b) {
        1099 return compare(a[key], b[key]);
        1100 });
        1101};
        1102
        1103
        1104/**
        1105 * Tells if the array is sorted.
        1106 * @param {!Array.<T>} arr The array.
        1107 * @param {?function(T,T):number=} opt_compareFn Function to compare the
        1108 * array elements.
        1109 * Should take 2 arguments to compare, and return a negative number, zero,
        1110 * or a positive number depending on whether the first argument is less
        1111 * than, equal to, or greater than the second.
        1112 * @param {boolean=} opt_strict If true no equal elements are allowed.
        1113 * @return {boolean} Whether the array is sorted.
        1114 * @template T
        1115 */
        1116goog.array.isSorted = function(arr, opt_compareFn, opt_strict) {
        1117 var compare = opt_compareFn || goog.array.defaultCompare;
        1118 for (var i = 1; i < arr.length; i++) {
        1119 var compareResult = compare(arr[i - 1], arr[i]);
        1120 if (compareResult > 0 || compareResult == 0 && opt_strict) {
        1121 return false;
        1122 }
        1123 }
        1124 return true;
        1125};
        1126
        1127
        1128/**
        1129 * Compares two arrays for equality. Two arrays are considered equal if they
        1130 * have the same length and their corresponding elements are equal according to
        1131 * the comparison function.
        1132 *
        1133 * @param {goog.array.ArrayLike} arr1 The first array to compare.
        1134 * @param {goog.array.ArrayLike} arr2 The second array to compare.
        1135 * @param {Function=} opt_equalsFn Optional comparison function.
        1136 * Should take 2 arguments to compare, and return true if the arguments
        1137 * are equal. Defaults to {@link goog.array.defaultCompareEquality} which
        1138 * compares the elements using the built-in '===' operator.
        1139 * @return {boolean} Whether the two arrays are equal.
        1140 */
        1141goog.array.equals = function(arr1, arr2, opt_equalsFn) {
        1142 if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||
        1143 arr1.length != arr2.length) {
        1144 return false;
        1145 }
        1146 var l = arr1.length;
        1147 var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
        1148 for (var i = 0; i < l; i++) {
        1149 if (!equalsFn(arr1[i], arr2[i])) {
        1150 return false;
        1151 }
        1152 }
        1153 return true;
        1154};
        1155
        1156
        1157/**
        1158 * @deprecated Use {@link goog.array.equals}.
        1159 * @param {goog.array.ArrayLike} arr1 See {@link goog.array.equals}.
        1160 * @param {goog.array.ArrayLike} arr2 See {@link goog.array.equals}.
        1161 * @param {Function=} opt_equalsFn See {@link goog.array.equals}.
        1162 * @return {boolean} See {@link goog.array.equals}.
        1163 */
        1164goog.array.compare = function(arr1, arr2, opt_equalsFn) {
        1165 return goog.array.equals(arr1, arr2, opt_equalsFn);
        1166};
        1167
        1168
        1169/**
        1170 * 3-way array compare function.
        1171 * @param {!goog.array.ArrayLike} arr1 The first array to compare.
        1172 * @param {!goog.array.ArrayLike} arr2 The second array to compare.
        1173 * @param {?function(?, ?): number=} opt_compareFn Optional comparison function
        1174 * by which the array is to be ordered. Should take 2 arguments to compare,
        1175 * and return a negative number, zero, or a positive number depending on
        1176 * whether the first argument is less than, equal to, or greater than the
        1177 * second.
        1178 * @return {number} Negative number, zero, or a positive number depending on
        1179 * whether the first argument is less than, equal to, or greater than the
        1180 * second.
        1181 */
        1182goog.array.compare3 = function(arr1, arr2, opt_compareFn) {
        1183 var compare = opt_compareFn || goog.array.defaultCompare;
        1184 var l = Math.min(arr1.length, arr2.length);
        1185 for (var i = 0; i < l; i++) {
        1186 var result = compare(arr1[i], arr2[i]);
        1187 if (result != 0) {
        1188 return result;
        1189 }
        1190 }
        1191 return goog.array.defaultCompare(arr1.length, arr2.length);
        1192};
        1193
        1194
        1195/**
        1196 * Compares its two arguments for order, using the built in < and >
        1197 * operators.
        1198 * @param {*} a The first object to be compared.
        1199 * @param {*} b The second object to be compared.
        1200 * @return {number} A negative number, zero, or a positive number as the first
        1201 * argument is less than, equal to, or greater than the second.
        1202 */
        1203goog.array.defaultCompare = function(a, b) {
        1204 return a > b ? 1 : a < b ? -1 : 0;
        1205};
        1206
        1207
        1208/**
        1209 * Compares its two arguments for equality, using the built in === operator.
        1210 * @param {*} a The first object to compare.
        1211 * @param {*} b The second object to compare.
        1212 * @return {boolean} True if the two arguments are equal, false otherwise.
        1213 */
        1214goog.array.defaultCompareEquality = function(a, b) {
        1215 return a === b;
        1216};
        1217
        1218
        1219/**
        1220 * Inserts a value into a sorted array. The array is not modified if the
        1221 * value is already present.
        1222 * @param {Array.<T>} array The array to modify.
        1223 * @param {T} value The object to insert.
        1224 * @param {?function(T,T):number=} opt_compareFn Optional comparison function by
        1225 * which the
        1226 * array is ordered. Should take 2 arguments to compare, and return a
        1227 * negative number, zero, or a positive number depending on whether the
        1228 * first argument is less than, equal to, or greater than the second.
        1229 * @return {boolean} True if an element was inserted.
        1230 * @template T
        1231 */
        1232goog.array.binaryInsert = function(array, value, opt_compareFn) {
        1233 var index = goog.array.binarySearch(array, value, opt_compareFn);
        1234 if (index < 0) {
        1235 goog.array.insertAt(array, value, -(index + 1));
        1236 return true;
        1237 }
        1238 return false;
        1239};
        1240
        1241
        1242/**
        1243 * Removes a value from a sorted array.
        1244 * @param {Array} array The array to modify.
        1245 * @param {*} value The object to remove.
        1246 * @param {Function=} opt_compareFn Optional comparison function by which the
        1247 * array is ordered. Should take 2 arguments to compare, and return a
        1248 * negative number, zero, or a positive number depending on whether the
        1249 * first argument is less than, equal to, or greater than the second.
        1250 * @return {boolean} True if an element was removed.
        1251 */
        1252goog.array.binaryRemove = function(array, value, opt_compareFn) {
        1253 var index = goog.array.binarySearch(array, value, opt_compareFn);
        1254 return (index >= 0) ? goog.array.removeAt(array, index) : false;
        1255};
        1256
        1257
        1258/**
        1259 * Splits an array into disjoint buckets according to a splitting function.
        1260 * @param {Array.<T>} array The array.
        1261 * @param {function(this:S, T,number,Array.<T>):?} sorter Function to call for
        1262 * every element. This takes 3 arguments (the element, the index and the
        1263 * array) and must return a valid object key (a string, number, etc), or
        1264 * undefined, if that object should not be placed in a bucket.
        1265 * @param {S=} opt_obj The object to be used as the value of 'this' within
        1266 * sorter.
        1267 * @return {!Object} An object, with keys being all of the unique return values
        1268 * of sorter, and values being arrays containing the items for
        1269 * which the splitter returned that key.
        1270 * @template T,S
        1271 */
        1272goog.array.bucket = function(array, sorter, opt_obj) {
        1273 var buckets = {};
        1274
        1275 for (var i = 0; i < array.length; i++) {
        1276 var value = array[i];
        1277 var key = sorter.call(opt_obj, value, i, array);
        1278 if (goog.isDef(key)) {
        1279 // Push the value to the right bucket, creating it if necessary.
        1280 var bucket = buckets[key] || (buckets[key] = []);
        1281 bucket.push(value);
        1282 }
        1283 }
        1284
        1285 return buckets;
        1286};
        1287
        1288
        1289/**
        1290 * Creates a new object built from the provided array and the key-generation
        1291 * function.
        1292 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over
        1293 * which to iterate whose elements will be the values in the new object.
        1294 * @param {?function(this:S, T, number, ?) : string} keyFunc The function to
        1295 * call for every element. This function takes 3 arguments (the element, the
        1296 * index and the array) and should return a string that will be used as the
        1297 * key for the element in the new object. If the function returns the same
        1298 * key for more than one element, the value for that key is
        1299 * implementation-defined.
        1300 * @param {S=} opt_obj The object to be used as the value of 'this'
        1301 * within keyFunc.
        1302 * @return {!Object.<T>} The new object.
        1303 * @template T,S
        1304 */
        1305goog.array.toObject = function(arr, keyFunc, opt_obj) {
        1306 var ret = {};
        1307 goog.array.forEach(arr, function(element, index) {
        1308 ret[keyFunc.call(opt_obj, element, index, arr)] = element;
        1309 });
        1310 return ret;
        1311};
        1312
        1313
        1314/**
        1315 * Creates a range of numbers in an arithmetic progression.
        1316 *
        1317 * Range takes 1, 2, or 3 arguments:
        1318 * <pre>
        1319 * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
        1320 * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
        1321 * range(-2, -5, -1) produces [-2, -3, -4]
        1322 * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
        1323 * </pre>
        1324 *
        1325 * @param {number} startOrEnd The starting value of the range if an end argument
        1326 * is provided. Otherwise, the start value is 0, and this is the end value.
        1327 * @param {number=} opt_end The optional end value of the range.
        1328 * @param {number=} opt_step The step size between range values. Defaults to 1
        1329 * if opt_step is undefined or 0.
        1330 * @return {!Array.<number>} An array of numbers for the requested range. May be
        1331 * an empty array if adding the step would not converge toward the end
        1332 * value.
        1333 */
        1334goog.array.range = function(startOrEnd, opt_end, opt_step) {
        1335 var array = [];
        1336 var start = 0;
        1337 var end = startOrEnd;
        1338 var step = opt_step || 1;
        1339 if (opt_end !== undefined) {
        1340 start = startOrEnd;
        1341 end = opt_end;
        1342 }
        1343
        1344 if (step * (end - start) < 0) {
        1345 // Sign mismatch: start + step will never reach the end value.
        1346 return [];
        1347 }
        1348
        1349 if (step > 0) {
        1350 for (var i = start; i < end; i += step) {
        1351 array.push(i);
        1352 }
        1353 } else {
        1354 for (var i = start; i > end; i += step) {
        1355 array.push(i);
        1356 }
        1357 }
        1358 return array;
        1359};
        1360
        1361
        1362/**
        1363 * Returns an array consisting of the given value repeated N times.
        1364 *
        1365 * @param {*} value The value to repeat.
        1366 * @param {number} n The repeat count.
        1367 * @return {!Array} An array with the repeated value.
        1368 */
        1369goog.array.repeat = function(value, n) {
        1370 var array = [];
        1371 for (var i = 0; i < n; i++) {
        1372 array[i] = value;
        1373 }
        1374 return array;
        1375};
        1376
        1377
        1378/**
        1379 * Returns an array consisting of every argument with all arrays
        1380 * expanded in-place recursively.
        1381 *
        1382 * @param {...*} var_args The values to flatten.
        1383 * @return {!Array} An array containing the flattened values.
        1384 */
        1385goog.array.flatten = function(var_args) {
        1386 var result = [];
        1387 for (var i = 0; i < arguments.length; i++) {
        1388 var element = arguments[i];
        1389 if (goog.isArray(element)) {
        1390 result.push.apply(result, goog.array.flatten.apply(null, element));
        1391 } else {
        1392 result.push(element);
        1393 }
        1394 }
        1395 return result;
        1396};
        1397
        1398
        1399/**
        1400 * Rotates an array in-place. After calling this method, the element at
        1401 * index i will be the element previously at index (i - n) %
        1402 * array.length, for all values of i between 0 and array.length - 1,
        1403 * inclusive.
        1404 *
        1405 * For example, suppose list comprises [t, a, n, k, s]. After invoking
        1406 * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].
        1407 *
        1408 * @param {!Array.<T>} array The array to rotate.
        1409 * @param {number} n The amount to rotate.
        1410 * @return {!Array.<T>} The array.
        1411 * @template T
        1412 */
        1413goog.array.rotate = function(array, n) {
        1414 goog.asserts.assert(array.length != null);
        1415
        1416 if (array.length) {
        1417 n %= array.length;
        1418 if (n > 0) {
        1419 goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n));
        1420 } else if (n < 0) {
        1421 goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n));
        1422 }
        1423 }
        1424 return array;
        1425};
        1426
        1427
        1428/**
        1429 * Creates a new array for which the element at position i is an array of the
        1430 * ith element of the provided arrays. The returned array will only be as long
        1431 * as the shortest array provided; additional values are ignored. For example,
        1432 * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
        1433 *
        1434 * This is similar to the zip() function in Python. See {@link
        1435 * http://docs.python.org/library/functions.html#zip}
        1436 *
        1437 * @param {...!goog.array.ArrayLike} var_args Arrays to be combined.
        1438 * @return {!Array.<!Array>} A new array of arrays created from provided arrays.
        1439 */
        1440goog.array.zip = function(var_args) {
        1441 if (!arguments.length) {
        1442 return [];
        1443 }
        1444 var result = [];
        1445 for (var i = 0; true; i++) {
        1446 var value = [];
        1447 for (var j = 0; j < arguments.length; j++) {
        1448 var arr = arguments[j];
        1449 // If i is larger than the array length, this is the shortest array.
        1450 if (i >= arr.length) {
        1451 return result;
        1452 }
        1453 value.push(arr[i]);
        1454 }
        1455 result.push(value);
        1456 }
        1457};
        1458
        1459
        1460/**
        1461 * Shuffles the values in the specified array using the Fisher-Yates in-place
        1462 * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
        1463 * and so resets the state of that random number generator. Similarly, may reset
        1464 * the state of the any other specified random number generator.
        1465 *
        1466 * Runtime: O(n)
        1467 *
        1468 * @param {!Array} arr The array to be shuffled.
        1469 * @param {function():number=} opt_randFn Optional random function to use for
        1470 * shuffling.
        1471 * Takes no arguments, and returns a random number on the interval [0, 1).
        1472 * Defaults to Math.random() using JavaScript's built-in Math library.
        1473 */
        1474goog.array.shuffle = function(arr, opt_randFn) {
        1475 var randFn = opt_randFn || Math.random;
        1476
        1477 for (var i = arr.length - 1; i > 0; i--) {
        1478 // Choose a random array index in [0, i] (inclusive with i).
        1479 var j = Math.floor(randFn() * (i + 1));
        1480
        1481 var tmp = arr[i];
        1482 arr[i] = arr[j];
        1483 arr[j] = tmp;
        1484 }
        1485};
        \ No newline at end of file +array.js

        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 * @author arv@google.com (Erik Arvidsson)
        19 */
        20
        21
        22goog.provide('goog.array');
        23goog.provide('goog.array.ArrayLike');
        24
        25goog.require('goog.asserts');
        26
        27
        28/**
        29 * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should
        30 * rely on Array.prototype functions, if available.
        31 *
        32 * The Array.prototype functions can be defined by external libraries like
        33 * Prototype and setting this flag to false forces closure to use its own
        34 * goog.array implementation.
        35 *
        36 * If your javascript can be loaded by a third party site and you are wary about
        37 * relying on the prototype functions, specify
        38 * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler.
        39 *
        40 * Setting goog.TRUSTED_SITE to false will automatically set
        41 * NATIVE_ARRAY_PROTOTYPES to false.
        42 */
        43goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);
        44
        45
        46/**
        47 * @define {boolean} If true, JSCompiler will use the native implementation of
        48 * array functions where appropriate (e.g., {@code Array#filter}) and remove the
        49 * unused pure JS implementation.
        50 */
        51goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false);
        52
        53
        54/**
        55 * @typedef {Array|NodeList|Arguments|{length: number}}
        56 */
        57goog.array.ArrayLike;
        58
        59
        60/**
        61 * Returns the last element in an array without removing it.
        62 * Same as goog.array.last.
        63 * @param {Array<T>|goog.array.ArrayLike} array The array.
        64 * @return {T} Last item in array.
        65 * @template T
        66 */
        67goog.array.peek = function(array) {
        68 return array[array.length - 1];
        69};
        70
        71
        72/**
        73 * Returns the last element in an array without removing it.
        74 * Same as goog.array.peek.
        75 * @param {Array<T>|goog.array.ArrayLike} array The array.
        76 * @return {T} Last item in array.
        77 * @template T
        78 */
        79goog.array.last = goog.array.peek;
        80
        81
        82/**
        83 * Reference to the original {@code Array.prototype}.
        84 * @private {!Object}
        85 */
        86goog.array.ARRAY_PROTOTYPE_ = Array.prototype;
        87
        88
        89// NOTE(arv): Since most of the array functions are generic it allows you to
        90// pass an array-like object. Strings have a length and are considered array-
        91// like. However, the 'in' operator does not work on strings so we cannot just
        92// use the array path even if the browser supports indexing into strings. We
        93// therefore end up splitting the string.
        94
        95
        96/**
        97 * Returns the index of the first element of an array with a specified value, or
        98 * -1 if the element is not present in the array.
        99 *
        100 * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}
        101 *
        102 * @param {Array<T>|goog.array.ArrayLike} arr The array to be searched.
        103 * @param {T} obj The object for which we are searching.
        104 * @param {number=} opt_fromIndex The index at which to start the search. If
        105 * omitted the search starts at index 0.
        106 * @return {number} The index of the first matching array element.
        107 * @template T
        108 */
        109goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
        110 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        111 goog.array.ARRAY_PROTOTYPE_.indexOf) ?
        112 function(arr, obj, opt_fromIndex) {
        113 goog.asserts.assert(arr.length != null);
        114
        115 return goog.array.ARRAY_PROTOTYPE_.indexOf.call(arr, obj, opt_fromIndex);
        116 } :
        117 function(arr, obj, opt_fromIndex) {
        118 var fromIndex = opt_fromIndex == null ?
        119 0 : (opt_fromIndex < 0 ?
        120 Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex);
        121
        122 if (goog.isString(arr)) {
        123 // Array.prototype.indexOf uses === so only strings should be found.
        124 if (!goog.isString(obj) || obj.length != 1) {
        125 return -1;
        126 }
        127 return arr.indexOf(obj, fromIndex);
        128 }
        129
        130 for (var i = fromIndex; i < arr.length; i++) {
        131 if (i in arr && arr[i] === obj)
        132 return i;
        133 }
        134 return -1;
        135 };
        136
        137
        138/**
        139 * Returns the index of the last element of an array with a specified value, or
        140 * -1 if the element is not present in the array.
        141 *
        142 * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}
        143 *
        144 * @param {!Array<T>|!goog.array.ArrayLike} arr The array to be searched.
        145 * @param {T} obj The object for which we are searching.
        146 * @param {?number=} opt_fromIndex The index at which to start the search. If
        147 * omitted the search starts at the end of the array.
        148 * @return {number} The index of the last matching array element.
        149 * @template T
        150 */
        151goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
        152 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        153 goog.array.ARRAY_PROTOTYPE_.lastIndexOf) ?
        154 function(arr, obj, opt_fromIndex) {
        155 goog.asserts.assert(arr.length != null);
        156
        157 // Firefox treats undefined and null as 0 in the fromIndex argument which
        158 // leads it to always return -1
        159 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
        160 return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(arr, obj, fromIndex);
        161 } :
        162 function(arr, obj, opt_fromIndex) {
        163 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
        164
        165 if (fromIndex < 0) {
        166 fromIndex = Math.max(0, arr.length + fromIndex);
        167 }
        168
        169 if (goog.isString(arr)) {
        170 // Array.prototype.lastIndexOf uses === so only strings should be found.
        171 if (!goog.isString(obj) || obj.length != 1) {
        172 return -1;
        173 }
        174 return arr.lastIndexOf(obj, fromIndex);
        175 }
        176
        177 for (var i = fromIndex; i >= 0; i--) {
        178 if (i in arr && arr[i] === obj)
        179 return i;
        180 }
        181 return -1;
        182 };
        183
        184
        185/**
        186 * Calls a function for each element in an array. Skips holes in the array.
        187 * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}
        188 *
        189 * @param {Array<T>|goog.array.ArrayLike} arr Array or array like object over
        190 * which to iterate.
        191 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
        192 * element. This function takes 3 arguments (the element, the index and the
        193 * array). The return value is ignored.
        194 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
        195 * @template T,S
        196 */
        197goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES &&
        198 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        199 goog.array.ARRAY_PROTOTYPE_.forEach) ?
        200 function(arr, f, opt_obj) {
        201 goog.asserts.assert(arr.length != null);
        202
        203 goog.array.ARRAY_PROTOTYPE_.forEach.call(arr, f, opt_obj);
        204 } :
        205 function(arr, f, opt_obj) {
        206 var l = arr.length; // must be fixed during loop... see docs
        207 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        208 for (var i = 0; i < l; i++) {
        209 if (i in arr2) {
        210 f.call(opt_obj, arr2[i], i, arr);
        211 }
        212 }
        213 };
        214
        215
        216/**
        217 * Calls a function for each element in an array, starting from the last
        218 * element rather than the first.
        219 *
        220 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        221 * like object over which to iterate.
        222 * @param {?function(this: S, T, number, ?): ?} f The function to call for every
        223 * element. This function
        224 * takes 3 arguments (the element, the index and the array). The return
        225 * value is ignored.
        226 * @param {S=} opt_obj The object to be used as the value of 'this'
        227 * within f.
        228 * @template T,S
        229 */
        230goog.array.forEachRight = function(arr, f, opt_obj) {
        231 var l = arr.length; // must be fixed during loop... see docs
        232 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        233 for (var i = l - 1; i >= 0; --i) {
        234 if (i in arr2) {
        235 f.call(opt_obj, arr2[i], i, arr);
        236 }
        237 }
        238};
        239
        240
        241/**
        242 * Calls a function for each element in an array, and if the function returns
        243 * true adds the element to a new array.
        244 *
        245 * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}
        246 *
        247 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        248 * like object over which to iterate.
        249 * @param {?function(this:S, T, number, ?):boolean} f The function to call for
        250 * every element. This function
        251 * takes 3 arguments (the element, the index and the array) and must
        252 * return a Boolean. If the return value is true the element is added to the
        253 * result array. If it is false the element is not included.
        254 * @param {S=} opt_obj The object to be used as the value of 'this'
        255 * within f.
        256 * @return {!Array<T>} a new array in which only elements that passed the test
        257 * are present.
        258 * @template T,S
        259 */
        260goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES &&
        261 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        262 goog.array.ARRAY_PROTOTYPE_.filter) ?
        263 function(arr, f, opt_obj) {
        264 goog.asserts.assert(arr.length != null);
        265
        266 return goog.array.ARRAY_PROTOTYPE_.filter.call(arr, f, opt_obj);
        267 } :
        268 function(arr, f, opt_obj) {
        269 var l = arr.length; // must be fixed during loop... see docs
        270 var res = [];
        271 var resLength = 0;
        272 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        273 for (var i = 0; i < l; i++) {
        274 if (i in arr2) {
        275 var val = arr2[i]; // in case f mutates arr2
        276 if (f.call(opt_obj, val, i, arr)) {
        277 res[resLength++] = val;
        278 }
        279 }
        280 }
        281 return res;
        282 };
        283
        284
        285/**
        286 * Calls a function for each element in an array and inserts the result into a
        287 * new array.
        288 *
        289 * See {@link http://tinyurl.com/developer-mozilla-org-array-map}
        290 *
        291 * @param {Array<VALUE>|goog.array.ArrayLike} arr Array or array like object
        292 * over which to iterate.
        293 * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call
        294 * for every element. This function takes 3 arguments (the element,
        295 * the index and the array) and should return something. The result will be
        296 * inserted into a new array.
        297 * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
        298 * @return {!Array<RESULT>} a new array with the results from f.
        299 * @template THIS, VALUE, RESULT
        300 */
        301goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES &&
        302 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        303 goog.array.ARRAY_PROTOTYPE_.map) ?
        304 function(arr, f, opt_obj) {
        305 goog.asserts.assert(arr.length != null);
        306
        307 return goog.array.ARRAY_PROTOTYPE_.map.call(arr, f, opt_obj);
        308 } :
        309 function(arr, f, opt_obj) {
        310 var l = arr.length; // must be fixed during loop... see docs
        311 var res = new Array(l);
        312 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        313 for (var i = 0; i < l; i++) {
        314 if (i in arr2) {
        315 res[i] = f.call(opt_obj, arr2[i], i, arr);
        316 }
        317 }
        318 return res;
        319 };
        320
        321
        322/**
        323 * Passes every element of an array into a function and accumulates the result.
        324 *
        325 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}
        326 *
        327 * For example:
        328 * var a = [1, 2, 3, 4];
        329 * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);
        330 * returns 10
        331 *
        332 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        333 * like object over which to iterate.
        334 * @param {function(this:S, R, T, number, ?) : R} f The function to call for
        335 * every element. This function
        336 * takes 4 arguments (the function's previous result or the initial value,
        337 * the value of the current array element, the current array index, and the
        338 * array itself)
        339 * function(previousValue, currentValue, index, array).
        340 * @param {?} val The initial value to pass into the function on the first call.
        341 * @param {S=} opt_obj The object to be used as the value of 'this'
        342 * within f.
        343 * @return {R} Result of evaluating f repeatedly across the values of the array.
        344 * @template T,S,R
        345 */
        346goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES &&
        347 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        348 goog.array.ARRAY_PROTOTYPE_.reduce) ?
        349 function(arr, f, val, opt_obj) {
        350 goog.asserts.assert(arr.length != null);
        351 if (opt_obj) {
        352 f = goog.bind(f, opt_obj);
        353 }
        354 return goog.array.ARRAY_PROTOTYPE_.reduce.call(arr, f, val);
        355 } :
        356 function(arr, f, val, opt_obj) {
        357 var rval = val;
        358 goog.array.forEach(arr, function(val, index) {
        359 rval = f.call(opt_obj, rval, val, index, arr);
        360 });
        361 return rval;
        362 };
        363
        364
        365/**
        366 * Passes every element of an array into a function and accumulates the result,
        367 * starting from the last element and working towards the first.
        368 *
        369 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}
        370 *
        371 * For example:
        372 * var a = ['a', 'b', 'c'];
        373 * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');
        374 * returns 'cba'
        375 *
        376 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        377 * like object over which to iterate.
        378 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for
        379 * every element. This function
        380 * takes 4 arguments (the function's previous result or the initial value,
        381 * the value of the current array element, the current array index, and the
        382 * array itself)
        383 * function(previousValue, currentValue, index, array).
        384 * @param {?} val The initial value to pass into the function on the first call.
        385 * @param {S=} opt_obj The object to be used as the value of 'this'
        386 * within f.
        387 * @return {R} Object returned as a result of evaluating f repeatedly across the
        388 * values of the array.
        389 * @template T,S,R
        390 */
        391goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES &&
        392 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        393 goog.array.ARRAY_PROTOTYPE_.reduceRight) ?
        394 function(arr, f, val, opt_obj) {
        395 goog.asserts.assert(arr.length != null);
        396 if (opt_obj) {
        397 f = goog.bind(f, opt_obj);
        398 }
        399 return goog.array.ARRAY_PROTOTYPE_.reduceRight.call(arr, f, val);
        400 } :
        401 function(arr, f, val, opt_obj) {
        402 var rval = val;
        403 goog.array.forEachRight(arr, function(val, index) {
        404 rval = f.call(opt_obj, rval, val, index, arr);
        405 });
        406 return rval;
        407 };
        408
        409
        410/**
        411 * Calls f for each element of an array. If any call returns true, some()
        412 * returns true (without checking the remaining elements). If all calls
        413 * return false, some() returns false.
        414 *
        415 * See {@link http://tinyurl.com/developer-mozilla-org-array-some}
        416 *
        417 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        418 * like object over which to iterate.
        419 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
        420 * for every element. This function takes 3 arguments (the element, the
        421 * index and the array) and should return a boolean.
        422 * @param {S=} opt_obj The object to be used as the value of 'this'
        423 * within f.
        424 * @return {boolean} true if any element passes the test.
        425 * @template T,S
        426 */
        427goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES &&
        428 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        429 goog.array.ARRAY_PROTOTYPE_.some) ?
        430 function(arr, f, opt_obj) {
        431 goog.asserts.assert(arr.length != null);
        432
        433 return goog.array.ARRAY_PROTOTYPE_.some.call(arr, f, opt_obj);
        434 } :
        435 function(arr, f, opt_obj) {
        436 var l = arr.length; // must be fixed during loop... see docs
        437 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        438 for (var i = 0; i < l; i++) {
        439 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
        440 return true;
        441 }
        442 }
        443 return false;
        444 };
        445
        446
        447/**
        448 * Call f for each element of an array. If all calls return true, every()
        449 * returns true. If any call returns false, every() returns false and
        450 * does not continue to check the remaining elements.
        451 *
        452 * See {@link http://tinyurl.com/developer-mozilla-org-array-every}
        453 *
        454 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        455 * like object over which to iterate.
        456 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
        457 * for every element. This function takes 3 arguments (the element, the
        458 * index and the array) and should return a boolean.
        459 * @param {S=} opt_obj The object to be used as the value of 'this'
        460 * within f.
        461 * @return {boolean} false if any element fails the test.
        462 * @template T,S
        463 */
        464goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES &&
        465 (goog.array.ASSUME_NATIVE_FUNCTIONS ||
        466 goog.array.ARRAY_PROTOTYPE_.every) ?
        467 function(arr, f, opt_obj) {
        468 goog.asserts.assert(arr.length != null);
        469
        470 return goog.array.ARRAY_PROTOTYPE_.every.call(arr, f, opt_obj);
        471 } :
        472 function(arr, f, opt_obj) {
        473 var l = arr.length; // must be fixed during loop... see docs
        474 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        475 for (var i = 0; i < l; i++) {
        476 if (i in arr2 && !f.call(opt_obj, arr2[i], i, arr)) {
        477 return false;
        478 }
        479 }
        480 return true;
        481 };
        482
        483
        484/**
        485 * Counts the array elements that fulfill the predicate, i.e. for which the
        486 * callback function returns true. Skips holes in the array.
        487 *
        488 * @param {!(Array<T>|goog.array.ArrayLike)} arr Array or array like object
        489 * over which to iterate.
        490 * @param {function(this: S, T, number, ?): boolean} f The function to call for
        491 * every element. Takes 3 arguments (the element, the index and the array).
        492 * @param {S=} opt_obj The object to be used as the value of 'this' within f.
        493 * @return {number} The number of the matching elements.
        494 * @template T,S
        495 */
        496goog.array.count = function(arr, f, opt_obj) {
        497 var count = 0;
        498 goog.array.forEach(arr, function(element, index, arr) {
        499 if (f.call(opt_obj, element, index, arr)) {
        500 ++count;
        501 }
        502 }, opt_obj);
        503 return count;
        504};
        505
        506
        507/**
        508 * Search an array for the first element that satisfies a given condition and
        509 * return that element.
        510 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        511 * like object over which to iterate.
        512 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        513 * for every element. This function takes 3 arguments (the element, the
        514 * index and the array) and should return a boolean.
        515 * @param {S=} opt_obj An optional "this" context for the function.
        516 * @return {T|null} The first array element that passes the test, or null if no
        517 * element is found.
        518 * @template T,S
        519 */
        520goog.array.find = function(arr, f, opt_obj) {
        521 var i = goog.array.findIndex(arr, f, opt_obj);
        522 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
        523};
        524
        525
        526/**
        527 * Search an array for the first element that satisfies a given condition and
        528 * return its index.
        529 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        530 * like object over which to iterate.
        531 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
        532 * every element. This function
        533 * takes 3 arguments (the element, the index and the array) and should
        534 * return a boolean.
        535 * @param {S=} opt_obj An optional "this" context for the function.
        536 * @return {number} The index of the first array element that passes the test,
        537 * or -1 if no element is found.
        538 * @template T,S
        539 */
        540goog.array.findIndex = function(arr, f, opt_obj) {
        541 var l = arr.length; // must be fixed during loop... see docs
        542 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        543 for (var i = 0; i < l; i++) {
        544 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
        545 return i;
        546 }
        547 }
        548 return -1;
        549};
        550
        551
        552/**
        553 * Search an array (in reverse order) for the last element that satisfies a
        554 * given condition and return that element.
        555 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        556 * like object over which to iterate.
        557 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        558 * for every element. This function
        559 * takes 3 arguments (the element, the index and the array) and should
        560 * return a boolean.
        561 * @param {S=} opt_obj An optional "this" context for the function.
        562 * @return {T|null} The last array element that passes the test, or null if no
        563 * element is found.
        564 * @template T,S
        565 */
        566goog.array.findRight = function(arr, f, opt_obj) {
        567 var i = goog.array.findIndexRight(arr, f, opt_obj);
        568 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];
        569};
        570
        571
        572/**
        573 * Search an array (in reverse order) for the last element that satisfies a
        574 * given condition and return its index.
        575 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        576 * like object over which to iterate.
        577 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        578 * for every element. This function
        579 * takes 3 arguments (the element, the index and the array) and should
        580 * return a boolean.
        581 * @param {S=} opt_obj An optional "this" context for the function.
        582 * @return {number} The index of the last array element that passes the test,
        583 * or -1 if no element is found.
        584 * @template T,S
        585 */
        586goog.array.findIndexRight = function(arr, f, opt_obj) {
        587 var l = arr.length; // must be fixed during loop... see docs
        588 var arr2 = goog.isString(arr) ? arr.split('') : arr;
        589 for (var i = l - 1; i >= 0; i--) {
        590 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {
        591 return i;
        592 }
        593 }
        594 return -1;
        595};
        596
        597
        598/**
        599 * Whether the array contains the given object.
        600 * @param {goog.array.ArrayLike} arr The array to test for the presence of the
        601 * element.
        602 * @param {*} obj The object for which to test.
        603 * @return {boolean} true if obj is present.
        604 */
        605goog.array.contains = function(arr, obj) {
        606 return goog.array.indexOf(arr, obj) >= 0;
        607};
        608
        609
        610/**
        611 * Whether the array is empty.
        612 * @param {goog.array.ArrayLike} arr The array to test.
        613 * @return {boolean} true if empty.
        614 */
        615goog.array.isEmpty = function(arr) {
        616 return arr.length == 0;
        617};
        618
        619
        620/**
        621 * Clears the array.
        622 * @param {goog.array.ArrayLike} arr Array or array like object to clear.
        623 */
        624goog.array.clear = function(arr) {
        625 // For non real arrays we don't have the magic length so we delete the
        626 // indices.
        627 if (!goog.isArray(arr)) {
        628 for (var i = arr.length - 1; i >= 0; i--) {
        629 delete arr[i];
        630 }
        631 }
        632 arr.length = 0;
        633};
        634
        635
        636/**
        637 * Pushes an item into an array, if it's not already in the array.
        638 * @param {Array<T>} arr Array into which to insert the item.
        639 * @param {T} obj Value to add.
        640 * @template T
        641 */
        642goog.array.insert = function(arr, obj) {
        643 if (!goog.array.contains(arr, obj)) {
        644 arr.push(obj);
        645 }
        646};
        647
        648
        649/**
        650 * Inserts an object at the given index of the array.
        651 * @param {goog.array.ArrayLike} arr The array to modify.
        652 * @param {*} obj The object to insert.
        653 * @param {number=} opt_i The index at which to insert the object. If omitted,
        654 * treated as 0. A negative index is counted from the end of the array.
        655 */
        656goog.array.insertAt = function(arr, obj, opt_i) {
        657 goog.array.splice(arr, opt_i, 0, obj);
        658};
        659
        660
        661/**
        662 * Inserts at the given index of the array, all elements of another array.
        663 * @param {goog.array.ArrayLike} arr The array to modify.
        664 * @param {goog.array.ArrayLike} elementsToAdd The array of elements to add.
        665 * @param {number=} opt_i The index at which to insert the object. If omitted,
        666 * treated as 0. A negative index is counted from the end of the array.
        667 */
        668goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {
        669 goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd);
        670};
        671
        672
        673/**
        674 * Inserts an object into an array before a specified object.
        675 * @param {Array<T>} arr The array to modify.
        676 * @param {T} obj The object to insert.
        677 * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2
        678 * is omitted or not found, obj is inserted at the end of the array.
        679 * @template T
        680 */
        681goog.array.insertBefore = function(arr, obj, opt_obj2) {
        682 var i;
        683 if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {
        684 arr.push(obj);
        685 } else {
        686 goog.array.insertAt(arr, obj, i);
        687 }
        688};
        689
        690
        691/**
        692 * Removes the first occurrence of a particular value from an array.
        693 * @param {Array<T>|goog.array.ArrayLike} arr Array from which to remove
        694 * value.
        695 * @param {T} obj Object to remove.
        696 * @return {boolean} True if an element was removed.
        697 * @template T
        698 */
        699goog.array.remove = function(arr, obj) {
        700 var i = goog.array.indexOf(arr, obj);
        701 var rv;
        702 if ((rv = i >= 0)) {
        703 goog.array.removeAt(arr, i);
        704 }
        705 return rv;
        706};
        707
        708
        709/**
        710 * Removes from an array the element at index i
        711 * @param {goog.array.ArrayLike} arr Array or array like object from which to
        712 * remove value.
        713 * @param {number} i The index to remove.
        714 * @return {boolean} True if an element was removed.
        715 */
        716goog.array.removeAt = function(arr, i) {
        717 goog.asserts.assert(arr.length != null);
        718
        719 // use generic form of splice
        720 // splice returns the removed items and if successful the length of that
        721 // will be 1
        722 return goog.array.ARRAY_PROTOTYPE_.splice.call(arr, i, 1).length == 1;
        723};
        724
        725
        726/**
        727 * Removes the first value that satisfies the given condition.
        728 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        729 * like object over which to iterate.
        730 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        731 * for every element. This function
        732 * takes 3 arguments (the element, the index and the array) and should
        733 * return a boolean.
        734 * @param {S=} opt_obj An optional "this" context for the function.
        735 * @return {boolean} True if an element was removed.
        736 * @template T,S
        737 */
        738goog.array.removeIf = function(arr, f, opt_obj) {
        739 var i = goog.array.findIndex(arr, f, opt_obj);
        740 if (i >= 0) {
        741 goog.array.removeAt(arr, i);
        742 return true;
        743 }
        744 return false;
        745};
        746
        747
        748/**
        749 * Removes all values that satisfy the given condition.
        750 * @param {Array<T>|goog.array.ArrayLike} arr Array or array
        751 * like object over which to iterate.
        752 * @param {?function(this:S, T, number, ?) : boolean} f The function to call
        753 * for every element. This function
        754 * takes 3 arguments (the element, the index and the array) and should
        755 * return a boolean.
        756 * @param {S=} opt_obj An optional "this" context for the function.
        757 * @return {number} The number of items removed
        758 * @template T,S
        759 */
        760goog.array.removeAllIf = function(arr, f, opt_obj) {
        761 var removedCount = 0;
        762 goog.array.forEachRight(arr, function(val, index) {
        763 if (f.call(opt_obj, val, index, arr)) {
        764 if (goog.array.removeAt(arr, index)) {
        765 removedCount++;
        766 }
        767 }
        768 });
        769 return removedCount;
        770};
        771
        772
        773/**
        774 * Returns a new array that is the result of joining the arguments. If arrays
        775 * are passed then their items are added, however, if non-arrays are passed they
        776 * will be added to the return array as is.
        777 *
        778 * Note that ArrayLike objects will be added as is, rather than having their
        779 * items added.
        780 *
        781 * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]
        782 * goog.array.concat(0, [1, 2]) -> [0, 1, 2]
        783 * goog.array.concat([1, 2], null) -> [1, 2, null]
        784 *
        785 * There is bug in all current versions of IE (6, 7 and 8) where arrays created
        786 * in an iframe become corrupted soon (not immediately) after the iframe is
        787 * destroyed. This is common if loading data via goog.net.IframeIo, for example.
        788 * This corruption only affects the concat method which will start throwing
        789 * Catastrophic Errors (#-2147418113).
        790 *
        791 * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.
        792 *
        793 * Internally goog.array should use this, so that all methods will continue to
        794 * work on these broken array objects.
        795 *
        796 * @param {...*} var_args Items to concatenate. Arrays will have each item
        797 * added, while primitives and objects will be added as is.
        798 * @return {!Array<?>} The new resultant array.
        799 */
        800goog.array.concat = function(var_args) {
        801 return goog.array.ARRAY_PROTOTYPE_.concat.apply(
        802 goog.array.ARRAY_PROTOTYPE_, arguments);
        803};
        804
        805
        806/**
        807 * Returns a new array that contains the contents of all the arrays passed.
        808 * @param {...!Array<T>} var_args
        809 * @return {!Array<T>}
        810 * @template T
        811 */
        812goog.array.join = function(var_args) {
        813 return goog.array.ARRAY_PROTOTYPE_.concat.apply(
        814 goog.array.ARRAY_PROTOTYPE_, arguments);
        815};
        816
        817
        818/**
        819 * Converts an object to an array.
        820 * @param {Array<T>|goog.array.ArrayLike} object The object to convert to an
        821 * array.
        822 * @return {!Array<T>} The object converted into an array. If object has a
        823 * length property, every property indexed with a non-negative number
        824 * less than length will be included in the result. If object does not
        825 * have a length property, an empty array will be returned.
        826 * @template T
        827 */
        828goog.array.toArray = function(object) {
        829 var length = object.length;
        830
        831 // If length is not a number the following it false. This case is kept for
        832 // backwards compatibility since there are callers that pass objects that are
        833 // not array like.
        834 if (length > 0) {
        835 var rv = new Array(length);
        836 for (var i = 0; i < length; i++) {
        837 rv[i] = object[i];
        838 }
        839 return rv;
        840 }
        841 return [];
        842};
        843
        844
        845/**
        846 * Does a shallow copy of an array.
        847 * @param {Array<T>|goog.array.ArrayLike} arr Array or array-like object to
        848 * clone.
        849 * @return {!Array<T>} Clone of the input array.
        850 * @template T
        851 */
        852goog.array.clone = goog.array.toArray;
        853
        854
        855/**
        856 * Extends an array with another array, element, or "array like" object.
        857 * This function operates 'in-place', it does not create a new Array.
        858 *
        859 * Example:
        860 * var a = [];
        861 * goog.array.extend(a, [0, 1]);
        862 * a; // [0, 1]
        863 * goog.array.extend(a, 2);
        864 * a; // [0, 1, 2]
        865 *
        866 * @param {Array<VALUE>} arr1 The array to modify.
        867 * @param {...(Array<VALUE>|VALUE)} var_args The elements or arrays of elements
        868 * to add to arr1.
        869 * @template VALUE
        870 */
        871goog.array.extend = function(arr1, var_args) {
        872 for (var i = 1; i < arguments.length; i++) {
        873 var arr2 = arguments[i];
        874 if (goog.isArrayLike(arr2)) {
        875 var len1 = arr1.length || 0;
        876 var len2 = arr2.length || 0;
        877 arr1.length = len1 + len2;
        878 for (var j = 0; j < len2; j++) {
        879 arr1[len1 + j] = arr2[j];
        880 }
        881 } else {
        882 arr1.push(arr2);
        883 }
        884 }
        885};
        886
        887
        888/**
        889 * Adds or removes elements from an array. This is a generic version of Array
        890 * splice. This means that it might work on other objects similar to arrays,
        891 * such as the arguments object.
        892 *
        893 * @param {Array<T>|goog.array.ArrayLike} arr The array to modify.
        894 * @param {number|undefined} index The index at which to start changing the
        895 * array. If not defined, treated as 0.
        896 * @param {number} howMany How many elements to remove (0 means no removal. A
        897 * value below 0 is treated as zero and so is any other non number. Numbers
        898 * are floored).
        899 * @param {...T} var_args Optional, additional elements to insert into the
        900 * array.
        901 * @return {!Array<T>} the removed elements.
        902 * @template T
        903 */
        904goog.array.splice = function(arr, index, howMany, var_args) {
        905 goog.asserts.assert(arr.length != null);
        906
        907 return goog.array.ARRAY_PROTOTYPE_.splice.apply(
        908 arr, goog.array.slice(arguments, 1));
        909};
        910
        911
        912/**
        913 * Returns a new array from a segment of an array. This is a generic version of
        914 * Array slice. This means that it might work on other objects similar to
        915 * arrays, such as the arguments object.
        916 *
        917 * @param {Array<T>|goog.array.ArrayLike} arr The array from
        918 * which to copy a segment.
        919 * @param {number} start The index of the first element to copy.
        920 * @param {number=} opt_end The index after the last element to copy.
        921 * @return {!Array<T>} A new array containing the specified segment of the
        922 * original array.
        923 * @template T
        924 */
        925goog.array.slice = function(arr, start, opt_end) {
        926 goog.asserts.assert(arr.length != null);
        927
        928 // passing 1 arg to slice is not the same as passing 2 where the second is
        929 // null or undefined (in that case the second argument is treated as 0).
        930 // we could use slice on the arguments object and then use apply instead of
        931 // testing the length
        932 if (arguments.length <= 2) {
        933 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start);
        934 } else {
        935 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end);
        936 }
        937};
        938
        939
        940/**
        941 * Removes all duplicates from an array (retaining only the first
        942 * occurrence of each array element). This function modifies the
        943 * array in place and doesn't change the order of the non-duplicate items.
        944 *
        945 * For objects, duplicates are identified as having the same unique ID as
        946 * defined by {@link goog.getUid}.
        947 *
        948 * Alternatively you can specify a custom hash function that returns a unique
        949 * value for each item in the array it should consider unique.
        950 *
        951 * Runtime: N,
        952 * Worstcase space: 2N (no dupes)
        953 *
        954 * @param {Array<T>|goog.array.ArrayLike} arr The array from which to remove
        955 * duplicates.
        956 * @param {Array=} opt_rv An optional array in which to return the results,
        957 * instead of performing the removal inplace. If specified, the original
        958 * array will remain unchanged.
        959 * @param {function(T):string=} opt_hashFn An optional function to use to
        960 * apply to every item in the array. This function should return a unique
        961 * value for each item in the array it should consider unique.
        962 * @template T
        963 */
        964goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) {
        965 var returnArray = opt_rv || arr;
        966 var defaultHashFn = function(item) {
        967 // Prefix each type with a single character representing the type to
        968 // prevent conflicting keys (e.g. true and 'true').
        969 return goog.isObject(item) ? 'o' + goog.getUid(item) :
        970 (typeof item).charAt(0) + item;
        971 };
        972 var hashFn = opt_hashFn || defaultHashFn;
        973
        974 var seen = {}, cursorInsert = 0, cursorRead = 0;
        975 while (cursorRead < arr.length) {
        976 var current = arr[cursorRead++];
        977 var key = hashFn(current);
        978 if (!Object.prototype.hasOwnProperty.call(seen, key)) {
        979 seen[key] = true;
        980 returnArray[cursorInsert++] = current;
        981 }
        982 }
        983 returnArray.length = cursorInsert;
        984};
        985
        986
        987/**
        988 * Searches the specified array for the specified target using the binary
        989 * search algorithm. If no opt_compareFn is specified, elements are compared
        990 * using <code>goog.array.defaultCompare</code>, which compares the elements
        991 * using the built in < and > operators. This will produce the expected
        992 * behavior for homogeneous arrays of String(s) and Number(s). The array
        993 * specified <b>must</b> be sorted in ascending order (as defined by the
        994 * comparison function). If the array is not sorted, results are undefined.
        995 * If the array contains multiple instances of the specified target value, any
        996 * of these instances may be found.
        997 *
        998 * Runtime: O(log n)
        999 *
        1000 * @param {Array<VALUE>|goog.array.ArrayLike} arr The array to be searched.
        1001 * @param {TARGET} target The sought value.
        1002 * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison
        1003 * function by which the array is ordered. Should take 2 arguments to
        1004 * compare, and return a negative number, zero, or a positive number
        1005 * depending on whether the first argument is less than, equal to, or
        1006 * greater than the second.
        1007 * @return {number} Lowest index of the target value if found, otherwise
        1008 * (-(insertion point) - 1). The insertion point is where the value should
        1009 * be inserted into arr to preserve the sorted property. Return value >= 0
        1010 * iff target is found.
        1011 * @template TARGET, VALUE
        1012 */
        1013goog.array.binarySearch = function(arr, target, opt_compareFn) {
        1014 return goog.array.binarySearch_(arr,
        1015 opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */,
        1016 target);
        1017};
        1018
        1019
        1020/**
        1021 * Selects an index in the specified array using the binary search algorithm.
        1022 * The evaluator receives an element and determines whether the desired index
        1023 * is before, at, or after it. The evaluator must be consistent (formally,
        1024 * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)
        1025 * must be monotonically non-increasing).
        1026 *
        1027 * Runtime: O(log n)
        1028 *
        1029 * @param {Array<VALUE>|goog.array.ArrayLike} arr The array to be searched.
        1030 * @param {function(this:THIS, VALUE, number, ?): number} evaluator
        1031 * Evaluator function that receives 3 arguments (the element, the index and
        1032 * the array). Should return a negative number, zero, or a positive number
        1033 * depending on whether the desired index is before, at, or after the
        1034 * element passed to it.
        1035 * @param {THIS=} opt_obj The object to be used as the value of 'this'
        1036 * within evaluator.
        1037 * @return {number} Index of the leftmost element matched by the evaluator, if
        1038 * such exists; otherwise (-(insertion point) - 1). The insertion point is
        1039 * the index of the first element for which the evaluator returns negative,
        1040 * or arr.length if no such element exists. The return value is non-negative
        1041 * iff a match is found.
        1042 * @template THIS, VALUE
        1043 */
        1044goog.array.binarySelect = function(arr, evaluator, opt_obj) {
        1045 return goog.array.binarySearch_(arr, evaluator, true /* isEvaluator */,
        1046 undefined /* opt_target */, opt_obj);
        1047};
        1048
        1049
        1050/**
        1051 * Implementation of a binary search algorithm which knows how to use both
        1052 * comparison functions and evaluators. If an evaluator is provided, will call
        1053 * the evaluator with the given optional data object, conforming to the
        1054 * interface defined in binarySelect. Otherwise, if a comparison function is
        1055 * provided, will call the comparison function against the given data object.
        1056 *
        1057 * This implementation purposefully does not use goog.bind or goog.partial for
        1058 * performance reasons.
        1059 *
        1060 * Runtime: O(log n)
        1061 *
        1062 * @param {Array<?>|goog.array.ArrayLike} arr The array to be searched.
        1063 * @param {function(?, ?, ?): number | function(?, ?): number} compareFn
        1064 * Either an evaluator or a comparison function, as defined by binarySearch
        1065 * and binarySelect above.
        1066 * @param {boolean} isEvaluator Whether the function is an evaluator or a
        1067 * comparison function.
        1068 * @param {?=} opt_target If the function is a comparison function, then
        1069 * this is the target to binary search for.
        1070 * @param {Object=} opt_selfObj If the function is an evaluator, this is an
        1071 * optional this object for the evaluator.
        1072 * @return {number} Lowest index of the target value if found, otherwise
        1073 * (-(insertion point) - 1). The insertion point is where the value should
        1074 * be inserted into arr to preserve the sorted property. Return value >= 0
        1075 * iff target is found.
        1076 * @private
        1077 */
        1078goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target,
        1079 opt_selfObj) {
        1080 var left = 0; // inclusive
        1081 var right = arr.length; // exclusive
        1082 var found;
        1083 while (left < right) {
        1084 var middle = (left + right) >> 1;
        1085 var compareResult;
        1086 if (isEvaluator) {
        1087 compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);
        1088 } else {
        1089 compareResult = compareFn(opt_target, arr[middle]);
        1090 }
        1091 if (compareResult > 0) {
        1092 left = middle + 1;
        1093 } else {
        1094 right = middle;
        1095 // We are looking for the lowest index so we can't return immediately.
        1096 found = !compareResult;
        1097 }
        1098 }
        1099 // left is the index if found, or the insertion point otherwise.
        1100 // ~left is a shorthand for -left - 1.
        1101 return found ? left : ~left;
        1102};
        1103
        1104
        1105/**
        1106 * Sorts the specified array into ascending order. If no opt_compareFn is
        1107 * specified, elements are compared using
        1108 * <code>goog.array.defaultCompare</code>, which compares the elements using
        1109 * the built in < and > operators. This will produce the expected behavior
        1110 * for homogeneous arrays of String(s) and Number(s), unlike the native sort,
        1111 * but will give unpredictable results for heterogenous lists of strings and
        1112 * numbers with different numbers of digits.
        1113 *
        1114 * This sort is not guaranteed to be stable.
        1115 *
        1116 * Runtime: Same as <code>Array.prototype.sort</code>
        1117 *
        1118 * @param {Array<T>} arr The array to be sorted.
        1119 * @param {?function(T,T):number=} opt_compareFn Optional comparison
        1120 * function by which the
        1121 * array is to be ordered. Should take 2 arguments to compare, and return a
        1122 * negative number, zero, or a positive number depending on whether the
        1123 * first argument is less than, equal to, or greater than the second.
        1124 * @template T
        1125 */
        1126goog.array.sort = function(arr, opt_compareFn) {
        1127 // TODO(arv): Update type annotation since null is not accepted.
        1128 arr.sort(opt_compareFn || goog.array.defaultCompare);
        1129};
        1130
        1131
        1132/**
        1133 * Sorts the specified array into ascending order in a stable way. If no
        1134 * opt_compareFn is specified, elements are compared using
        1135 * <code>goog.array.defaultCompare</code>, which compares the elements using
        1136 * the built in < and > operators. This will produce the expected behavior
        1137 * for homogeneous arrays of String(s) and Number(s).
        1138 *
        1139 * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional
        1140 * O(n) overhead of copying the array twice.
        1141 *
        1142 * @param {Array<T>} arr The array to be sorted.
        1143 * @param {?function(T, T): number=} opt_compareFn Optional comparison function
        1144 * by which the array is to be ordered. Should take 2 arguments to compare,
        1145 * and return a negative number, zero, or a positive number depending on
        1146 * whether the first argument is less than, equal to, or greater than the
        1147 * second.
        1148 * @template T
        1149 */
        1150goog.array.stableSort = function(arr, opt_compareFn) {
        1151 for (var i = 0; i < arr.length; i++) {
        1152 arr[i] = {index: i, value: arr[i]};
        1153 }
        1154 var valueCompareFn = opt_compareFn || goog.array.defaultCompare;
        1155 function stableCompareFn(obj1, obj2) {
        1156 return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;
        1157 };
        1158 goog.array.sort(arr, stableCompareFn);
        1159 for (var i = 0; i < arr.length; i++) {
        1160 arr[i] = arr[i].value;
        1161 }
        1162};
        1163
        1164
        1165/**
        1166 * Sort the specified array into ascending order based on item keys
        1167 * returned by the specified key function.
        1168 * If no opt_compareFn is specified, the keys are compared in ascending order
        1169 * using <code>goog.array.defaultCompare</code>.
        1170 *
        1171 * Runtime: O(S(f(n)), where S is runtime of <code>goog.array.sort</code>
        1172 * and f(n) is runtime of the key function.
        1173 *
        1174 * @param {Array<T>} arr The array to be sorted.
        1175 * @param {function(T): K} keyFn Function taking array element and returning
        1176 * a key used for sorting this element.
        1177 * @param {?function(K, K): number=} opt_compareFn Optional comparison function
        1178 * by which the keys are to be ordered. Should take 2 arguments to compare,
        1179 * and return a negative number, zero, or a positive number depending on
        1180 * whether the first argument is less than, equal to, or greater than the
        1181 * second.
        1182 * @template T,K
        1183 */
        1184goog.array.sortByKey = function(arr, keyFn, opt_compareFn) {
        1185 var keyCompareFn = opt_compareFn || goog.array.defaultCompare;
        1186 goog.array.sort(arr, function(a, b) {
        1187 return keyCompareFn(keyFn(a), keyFn(b));
        1188 });
        1189};
        1190
        1191
        1192/**
        1193 * Sorts an array of objects by the specified object key and compare
        1194 * function. If no compare function is provided, the key values are
        1195 * compared in ascending order using <code>goog.array.defaultCompare</code>.
        1196 * This won't work for keys that get renamed by the compiler. So use
        1197 * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
        1198 * @param {Array<Object>} arr An array of objects to sort.
        1199 * @param {string} key The object key to sort by.
        1200 * @param {Function=} opt_compareFn The function to use to compare key
        1201 * values.
        1202 */
        1203goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {
        1204 goog.array.sortByKey(arr,
        1205 function(obj) { return obj[key]; },
        1206 opt_compareFn);
        1207};
        1208
        1209
        1210/**
        1211 * Tells if the array is sorted.
        1212 * @param {!Array<T>} arr The array.
        1213 * @param {?function(T,T):number=} opt_compareFn Function to compare the
        1214 * array elements.
        1215 * Should take 2 arguments to compare, and return a negative number, zero,
        1216 * or a positive number depending on whether the first argument is less
        1217 * than, equal to, or greater than the second.
        1218 * @param {boolean=} opt_strict If true no equal elements are allowed.
        1219 * @return {boolean} Whether the array is sorted.
        1220 * @template T
        1221 */
        1222goog.array.isSorted = function(arr, opt_compareFn, opt_strict) {
        1223 var compare = opt_compareFn || goog.array.defaultCompare;
        1224 for (var i = 1; i < arr.length; i++) {
        1225 var compareResult = compare(arr[i - 1], arr[i]);
        1226 if (compareResult > 0 || compareResult == 0 && opt_strict) {
        1227 return false;
        1228 }
        1229 }
        1230 return true;
        1231};
        1232
        1233
        1234/**
        1235 * Compares two arrays for equality. Two arrays are considered equal if they
        1236 * have the same length and their corresponding elements are equal according to
        1237 * the comparison function.
        1238 *
        1239 * @param {goog.array.ArrayLike} arr1 The first array to compare.
        1240 * @param {goog.array.ArrayLike} arr2 The second array to compare.
        1241 * @param {Function=} opt_equalsFn Optional comparison function.
        1242 * Should take 2 arguments to compare, and return true if the arguments
        1243 * are equal. Defaults to {@link goog.array.defaultCompareEquality} which
        1244 * compares the elements using the built-in '===' operator.
        1245 * @return {boolean} Whether the two arrays are equal.
        1246 */
        1247goog.array.equals = function(arr1, arr2, opt_equalsFn) {
        1248 if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||
        1249 arr1.length != arr2.length) {
        1250 return false;
        1251 }
        1252 var l = arr1.length;
        1253 var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
        1254 for (var i = 0; i < l; i++) {
        1255 if (!equalsFn(arr1[i], arr2[i])) {
        1256 return false;
        1257 }
        1258 }
        1259 return true;
        1260};
        1261
        1262
        1263/**
        1264 * 3-way array compare function.
        1265 * @param {!Array<VALUE>|!goog.array.ArrayLike} arr1 The first array to
        1266 * compare.
        1267 * @param {!Array<VALUE>|!goog.array.ArrayLike} arr2 The second array to
        1268 * compare.
        1269 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
        1270 * function by which the array is to be ordered. Should take 2 arguments to
        1271 * compare, and return a negative number, zero, or a positive number
        1272 * depending on whether the first argument is less than, equal to, or
        1273 * greater than the second.
        1274 * @return {number} Negative number, zero, or a positive number depending on
        1275 * whether the first argument is less than, equal to, or greater than the
        1276 * second.
        1277 * @template VALUE
        1278 */
        1279goog.array.compare3 = function(arr1, arr2, opt_compareFn) {
        1280 var compare = opt_compareFn || goog.array.defaultCompare;
        1281 var l = Math.min(arr1.length, arr2.length);
        1282 for (var i = 0; i < l; i++) {
        1283 var result = compare(arr1[i], arr2[i]);
        1284 if (result != 0) {
        1285 return result;
        1286 }
        1287 }
        1288 return goog.array.defaultCompare(arr1.length, arr2.length);
        1289};
        1290
        1291
        1292/**
        1293 * Compares its two arguments for order, using the built in < and >
        1294 * operators.
        1295 * @param {VALUE} a The first object to be compared.
        1296 * @param {VALUE} b The second object to be compared.
        1297 * @return {number} A negative number, zero, or a positive number as the first
        1298 * argument is less than, equal to, or greater than the second,
        1299 * respectively.
        1300 * @template VALUE
        1301 */
        1302goog.array.defaultCompare = function(a, b) {
        1303 return a > b ? 1 : a < b ? -1 : 0;
        1304};
        1305
        1306
        1307/**
        1308 * Compares its two arguments for inverse order, using the built in < and >
        1309 * operators.
        1310 * @param {VALUE} a The first object to be compared.
        1311 * @param {VALUE} b The second object to be compared.
        1312 * @return {number} A negative number, zero, or a positive number as the first
        1313 * argument is greater than, equal to, or less than the second,
        1314 * respectively.
        1315 * @template VALUE
        1316 */
        1317goog.array.inverseDefaultCompare = function(a, b) {
        1318 return -goog.array.defaultCompare(a, b);
        1319};
        1320
        1321
        1322/**
        1323 * Compares its two arguments for equality, using the built in === operator.
        1324 * @param {*} a The first object to compare.
        1325 * @param {*} b The second object to compare.
        1326 * @return {boolean} True if the two arguments are equal, false otherwise.
        1327 */
        1328goog.array.defaultCompareEquality = function(a, b) {
        1329 return a === b;
        1330};
        1331
        1332
        1333/**
        1334 * Inserts a value into a sorted array. The array is not modified if the
        1335 * value is already present.
        1336 * @param {Array<VALUE>|goog.array.ArrayLike} array The array to modify.
        1337 * @param {VALUE} value The object to insert.
        1338 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
        1339 * function by which the array is ordered. Should take 2 arguments to
        1340 * compare, and return a negative number, zero, or a positive number
        1341 * depending on whether the first argument is less than, equal to, or
        1342 * greater than the second.
        1343 * @return {boolean} True if an element was inserted.
        1344 * @template VALUE
        1345 */
        1346goog.array.binaryInsert = function(array, value, opt_compareFn) {
        1347 var index = goog.array.binarySearch(array, value, opt_compareFn);
        1348 if (index < 0) {
        1349 goog.array.insertAt(array, value, -(index + 1));
        1350 return true;
        1351 }
        1352 return false;
        1353};
        1354
        1355
        1356/**
        1357 * Removes a value from a sorted array.
        1358 * @param {!Array<VALUE>|!goog.array.ArrayLike} array The array to modify.
        1359 * @param {VALUE} value The object to remove.
        1360 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
        1361 * function by which the array is ordered. Should take 2 arguments to
        1362 * compare, and return a negative number, zero, or a positive number
        1363 * depending on whether the first argument is less than, equal to, or
        1364 * greater than the second.
        1365 * @return {boolean} True if an element was removed.
        1366 * @template VALUE
        1367 */
        1368goog.array.binaryRemove = function(array, value, opt_compareFn) {
        1369 var index = goog.array.binarySearch(array, value, opt_compareFn);
        1370 return (index >= 0) ? goog.array.removeAt(array, index) : false;
        1371};
        1372
        1373
        1374/**
        1375 * Splits an array into disjoint buckets according to a splitting function.
        1376 * @param {Array<T>} array The array.
        1377 * @param {function(this:S, T,number,Array<T>):?} sorter Function to call for
        1378 * every element. This takes 3 arguments (the element, the index and the
        1379 * array) and must return a valid object key (a string, number, etc), or
        1380 * undefined, if that object should not be placed in a bucket.
        1381 * @param {S=} opt_obj The object to be used as the value of 'this' within
        1382 * sorter.
        1383 * @return {!Object} An object, with keys being all of the unique return values
        1384 * of sorter, and values being arrays containing the items for
        1385 * which the splitter returned that key.
        1386 * @template T,S
        1387 */
        1388goog.array.bucket = function(array, sorter, opt_obj) {
        1389 var buckets = {};
        1390
        1391 for (var i = 0; i < array.length; i++) {
        1392 var value = array[i];
        1393 var key = sorter.call(opt_obj, value, i, array);
        1394 if (goog.isDef(key)) {
        1395 // Push the value to the right bucket, creating it if necessary.
        1396 var bucket = buckets[key] || (buckets[key] = []);
        1397 bucket.push(value);
        1398 }
        1399 }
        1400
        1401 return buckets;
        1402};
        1403
        1404
        1405/**
        1406 * Creates a new object built from the provided array and the key-generation
        1407 * function.
        1408 * @param {Array<T>|goog.array.ArrayLike} arr Array or array like object over
        1409 * which to iterate whose elements will be the values in the new object.
        1410 * @param {?function(this:S, T, number, ?) : string} keyFunc The function to
        1411 * call for every element. This function takes 3 arguments (the element, the
        1412 * index and the array) and should return a string that will be used as the
        1413 * key for the element in the new object. If the function returns the same
        1414 * key for more than one element, the value for that key is
        1415 * implementation-defined.
        1416 * @param {S=} opt_obj The object to be used as the value of 'this'
        1417 * within keyFunc.
        1418 * @return {!Object<T>} The new object.
        1419 * @template T,S
        1420 */
        1421goog.array.toObject = function(arr, keyFunc, opt_obj) {
        1422 var ret = {};
        1423 goog.array.forEach(arr, function(element, index) {
        1424 ret[keyFunc.call(opt_obj, element, index, arr)] = element;
        1425 });
        1426 return ret;
        1427};
        1428
        1429
        1430/**
        1431 * Creates a range of numbers in an arithmetic progression.
        1432 *
        1433 * Range takes 1, 2, or 3 arguments:
        1434 * <pre>
        1435 * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
        1436 * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
        1437 * range(-2, -5, -1) produces [-2, -3, -4]
        1438 * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
        1439 * </pre>
        1440 *
        1441 * @param {number} startOrEnd The starting value of the range if an end argument
        1442 * is provided. Otherwise, the start value is 0, and this is the end value.
        1443 * @param {number=} opt_end The optional end value of the range.
        1444 * @param {number=} opt_step The step size between range values. Defaults to 1
        1445 * if opt_step is undefined or 0.
        1446 * @return {!Array<number>} An array of numbers for the requested range. May be
        1447 * an empty array if adding the step would not converge toward the end
        1448 * value.
        1449 */
        1450goog.array.range = function(startOrEnd, opt_end, opt_step) {
        1451 var array = [];
        1452 var start = 0;
        1453 var end = startOrEnd;
        1454 var step = opt_step || 1;
        1455 if (opt_end !== undefined) {
        1456 start = startOrEnd;
        1457 end = opt_end;
        1458 }
        1459
        1460 if (step * (end - start) < 0) {
        1461 // Sign mismatch: start + step will never reach the end value.
        1462 return [];
        1463 }
        1464
        1465 if (step > 0) {
        1466 for (var i = start; i < end; i += step) {
        1467 array.push(i);
        1468 }
        1469 } else {
        1470 for (var i = start; i > end; i += step) {
        1471 array.push(i);
        1472 }
        1473 }
        1474 return array;
        1475};
        1476
        1477
        1478/**
        1479 * Returns an array consisting of the given value repeated N times.
        1480 *
        1481 * @param {VALUE} value The value to repeat.
        1482 * @param {number} n The repeat count.
        1483 * @return {!Array<VALUE>} An array with the repeated value.
        1484 * @template VALUE
        1485 */
        1486goog.array.repeat = function(value, n) {
        1487 var array = [];
        1488 for (var i = 0; i < n; i++) {
        1489 array[i] = value;
        1490 }
        1491 return array;
        1492};
        1493
        1494
        1495/**
        1496 * Returns an array consisting of every argument with all arrays
        1497 * expanded in-place recursively.
        1498 *
        1499 * @param {...*} var_args The values to flatten.
        1500 * @return {!Array<?>} An array containing the flattened values.
        1501 */
        1502goog.array.flatten = function(var_args) {
        1503 var CHUNK_SIZE = 8192;
        1504
        1505 var result = [];
        1506 for (var i = 0; i < arguments.length; i++) {
        1507 var element = arguments[i];
        1508 if (goog.isArray(element)) {
        1509 for (var c = 0; c < element.length; c += CHUNK_SIZE) {
        1510 var chunk = goog.array.slice(element, c, c + CHUNK_SIZE);
        1511 var recurseResult = goog.array.flatten.apply(null, chunk);
        1512 for (var r = 0; r < recurseResult.length; r++) {
        1513 result.push(recurseResult[r]);
        1514 }
        1515 }
        1516 } else {
        1517 result.push(element);
        1518 }
        1519 }
        1520 return result;
        1521};
        1522
        1523
        1524/**
        1525 * Rotates an array in-place. After calling this method, the element at
        1526 * index i will be the element previously at index (i - n) %
        1527 * array.length, for all values of i between 0 and array.length - 1,
        1528 * inclusive.
        1529 *
        1530 * For example, suppose list comprises [t, a, n, k, s]. After invoking
        1531 * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].
        1532 *
        1533 * @param {!Array<T>} array The array to rotate.
        1534 * @param {number} n The amount to rotate.
        1535 * @return {!Array<T>} The array.
        1536 * @template T
        1537 */
        1538goog.array.rotate = function(array, n) {
        1539 goog.asserts.assert(array.length != null);
        1540
        1541 if (array.length) {
        1542 n %= array.length;
        1543 if (n > 0) {
        1544 goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n));
        1545 } else if (n < 0) {
        1546 goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n));
        1547 }
        1548 }
        1549 return array;
        1550};
        1551
        1552
        1553/**
        1554 * Moves one item of an array to a new position keeping the order of the rest
        1555 * of the items. Example use case: keeping a list of JavaScript objects
        1556 * synchronized with the corresponding list of DOM elements after one of the
        1557 * elements has been dragged to a new position.
        1558 * @param {!(Array|Arguments|{length:number})} arr The array to modify.
        1559 * @param {number} fromIndex Index of the item to move between 0 and
        1560 * {@code arr.length - 1}.
        1561 * @param {number} toIndex Target index between 0 and {@code arr.length - 1}.
        1562 */
        1563goog.array.moveItem = function(arr, fromIndex, toIndex) {
        1564 goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length);
        1565 goog.asserts.assert(toIndex >= 0 && toIndex < arr.length);
        1566 // Remove 1 item at fromIndex.
        1567 var removedItems = goog.array.ARRAY_PROTOTYPE_.splice.call(arr, fromIndex, 1);
        1568 // Insert the removed item at toIndex.
        1569 goog.array.ARRAY_PROTOTYPE_.splice.call(arr, toIndex, 0, removedItems[0]);
        1570 // We don't use goog.array.insertAt and goog.array.removeAt, because they're
        1571 // significantly slower than splice.
        1572};
        1573
        1574
        1575/**
        1576 * Creates a new array for which the element at position i is an array of the
        1577 * ith element of the provided arrays. The returned array will only be as long
        1578 * as the shortest array provided; additional values are ignored. For example,
        1579 * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
        1580 *
        1581 * This is similar to the zip() function in Python. See {@link
        1582 * http://docs.python.org/library/functions.html#zip}
        1583 *
        1584 * @param {...!goog.array.ArrayLike} var_args Arrays to be combined.
        1585 * @return {!Array<!Array<?>>} A new array of arrays created from
        1586 * provided arrays.
        1587 */
        1588goog.array.zip = function(var_args) {
        1589 if (!arguments.length) {
        1590 return [];
        1591 }
        1592 var result = [];
        1593 var minLen = arguments[0].length;
        1594 for (var i = 1; i < arguments.length; i++) {
        1595 if (arguments[i].length < minLen) {
        1596 minLen = arguments[i].length;
        1597 }
        1598 }
        1599 for (var i = 0; i < minLen; i++) {
        1600 var value = [];
        1601 for (var j = 0; j < arguments.length; j++) {
        1602 value.push(arguments[j][i]);
        1603 }
        1604 result.push(value);
        1605 }
        1606 return result;
        1607};
        1608
        1609
        1610/**
        1611 * Shuffles the values in the specified array using the Fisher-Yates in-place
        1612 * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
        1613 * and so resets the state of that random number generator. Similarly, may reset
        1614 * the state of the any other specified random number generator.
        1615 *
        1616 * Runtime: O(n)
        1617 *
        1618 * @param {!Array<?>} arr The array to be shuffled.
        1619 * @param {function():number=} opt_randFn Optional random function to use for
        1620 * shuffling.
        1621 * Takes no arguments, and returns a random number on the interval [0, 1).
        1622 * Defaults to Math.random() using JavaScript's built-in Math library.
        1623 */
        1624goog.array.shuffle = function(arr, opt_randFn) {
        1625 var randFn = opt_randFn || Math.random;
        1626
        1627 for (var i = arr.length - 1; i > 0; i--) {
        1628 // Choose a random array index in [0, i] (inclusive with i).
        1629 var j = Math.floor(randFn() * (i + 1));
        1630
        1631 var tmp = arr[i];
        1632 arr[i] = arr[j];
        1633 arr[j] = tmp;
        1634 }
        1635};
        1636
        1637
        1638/**
        1639 * Returns a new array of elements from arr, based on the indexes of elements
        1640 * provided by index_arr. For example, the result of index copying
        1641 * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c'].
        1642 *
        1643 * @param {!Array<T>} arr The array to get a indexed copy from.
        1644 * @param {!Array<number>} index_arr An array of indexes to get from arr.
        1645 * @return {!Array<T>} A new array of elements from arr in index_arr order.
        1646 * @template T
        1647 */
        1648goog.array.copyByIndex = function(arr, index_arr) {
        1649 var result = [];
        1650 goog.array.forEach(index_arr, function(index) {
        1651 result.push(arr[index]);
        1652 });
        1653 return result;
        1654};
        \ No newline at end of file diff --git a/docs/source/lib/goog/asserts/asserts.js.src.html b/docs/source/lib/goog/asserts/asserts.js.src.html index a4d719f..afed314 100644 --- a/docs/source/lib/goog/asserts/asserts.js.src.html +++ b/docs/source/lib/goog/asserts/asserts.js.src.html @@ -1 +1 @@ -asserts.js

        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
        36goog.provide('goog.asserts');
        37goog.provide('goog.asserts.AssertionError');
        38
        39goog.require('goog.debug.Error');
        40goog.require('goog.string');
        41
        42
        43/**
        44 * @define {boolean} Whether to strip out asserts or to leave them in.
        45 */
        46goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);
        47
        48
        49
        50/**
        51 * Error object for failed assertions.
        52 * @param {string} messagePattern The pattern that was used to form message.
        53 * @param {!Array.<*>} messageArgs The items to substitute into the pattern.
        54 * @constructor
        55 * @extends {goog.debug.Error}
        56 */
        57goog.asserts.AssertionError = function(messagePattern, messageArgs) {
        58 messageArgs.unshift(messagePattern);
        59 goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
        60 // Remove the messagePattern afterwards to avoid permenantly modifying the
        61 // passed in array.
        62 messageArgs.shift();
        63
        64 /**
        65 * The message pattern used to format the error message. Error handlers can
        66 * use this to uniquely identify the assertion.
        67 * @type {string}
        68 */
        69 this.messagePattern = messagePattern;
        70};
        71goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
        72
        73
        74/** @override */
        75goog.asserts.AssertionError.prototype.name = 'AssertionError';
        76
        77
        78/**
        79 * Throws an exception with the given message and "Assertion failed" prefixed
        80 * onto it.
        81 * @param {string} defaultMessage The message to use if givenMessage is empty.
        82 * @param {Array.<*>} defaultArgs The substitution arguments for defaultMessage.
        83 * @param {string|undefined} givenMessage Message supplied by the caller.
        84 * @param {Array.<*>} givenArgs The substitution arguments for givenMessage.
        85 * @throws {goog.asserts.AssertionError} When the value is not a number.
        86 * @private
        87 */
        88goog.asserts.doAssertFailure_ =
        89 function(defaultMessage, defaultArgs, givenMessage, givenArgs) {
        90 var message = 'Assertion failed';
        91 if (givenMessage) {
        92 message += ': ' + givenMessage;
        93 var args = givenArgs;
        94 } else if (defaultMessage) {
        95 message += ': ' + defaultMessage;
        96 args = defaultArgs;
        97 }
        98 // The '' + works around an Opera 10 bug in the unit tests. Without it,
        99 // a stack trace is added to var message above. With this, a stack trace is
        100 // not added until this line (it causes the extra garbage to be added after
        101 // the assertion message instead of in the middle of it).
        102 throw new goog.asserts.AssertionError('' + message, args || []);
        103};
        104
        105
        106/**
        107 * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
        108 * true.
        109 * @param {*} condition The condition to check.
        110 * @param {string=} opt_message Error message in case of failure.
        111 * @param {...*} var_args The items to substitute into the failure message.
        112 * @return {*} The value of the condition.
        113 * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
        114 */
        115goog.asserts.assert = function(condition, opt_message, var_args) {
        116 if (goog.asserts.ENABLE_ASSERTS && !condition) {
        117 goog.asserts.doAssertFailure_('', null, opt_message,
        118 Array.prototype.slice.call(arguments, 2));
        119 }
        120 return condition;
        121};
        122
        123
        124/**
        125 * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
        126 * when we want to add a check in the unreachable area like switch-case
        127 * statement:
        128 *
        129 * <pre>
        130 * switch(type) {
        131 * case FOO: doSomething(); break;
        132 * case BAR: doSomethingElse(); break;
        133 * default: goog.assert.fail('Unrecognized type: ' + type);
        134 * // We have only 2 types - "default:" section is unreachable code.
        135 * }
        136 * </pre>
        137 *
        138 * @param {string=} opt_message Error message in case of failure.
        139 * @param {...*} var_args The items to substitute into the failure message.
        140 * @throws {goog.asserts.AssertionError} Failure.
        141 */
        142goog.asserts.fail = function(opt_message, var_args) {
        143 if (goog.asserts.ENABLE_ASSERTS) {
        144 throw new goog.asserts.AssertionError(
        145 'Failure' + (opt_message ? ': ' + opt_message : ''),
        146 Array.prototype.slice.call(arguments, 1));
        147 }
        148};
        149
        150
        151/**
        152 * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
        153 * @param {*} value The value to check.
        154 * @param {string=} opt_message Error message in case of failure.
        155 * @param {...*} var_args The items to substitute into the failure message.
        156 * @return {number} The value, guaranteed to be a number when asserts enabled.
        157 * @throws {goog.asserts.AssertionError} When the value is not a number.
        158 */
        159goog.asserts.assertNumber = function(value, opt_message, var_args) {
        160 if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) {
        161 goog.asserts.doAssertFailure_('Expected number but got %s: %s.',
        162 [goog.typeOf(value), value], opt_message,
        163 Array.prototype.slice.call(arguments, 2));
        164 }
        165 return /** @type {number} */ (value);
        166};
        167
        168
        169/**
        170 * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
        171 * @param {*} value The value to check.
        172 * @param {string=} opt_message Error message in case of failure.
        173 * @param {...*} var_args The items to substitute into the failure message.
        174 * @return {string} The value, guaranteed to be a string when asserts enabled.
        175 * @throws {goog.asserts.AssertionError} When the value is not a string.
        176 */
        177goog.asserts.assertString = function(value, opt_message, var_args) {
        178 if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) {
        179 goog.asserts.doAssertFailure_('Expected string but got %s: %s.',
        180 [goog.typeOf(value), value], opt_message,
        181 Array.prototype.slice.call(arguments, 2));
        182 }
        183 return /** @type {string} */ (value);
        184};
        185
        186
        187/**
        188 * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
        189 * @param {*} value The value to check.
        190 * @param {string=} opt_message Error message in case of failure.
        191 * @param {...*} var_args The items to substitute into the failure message.
        192 * @return {!Function} The value, guaranteed to be a function when asserts
        193 * enabled.
        194 * @throws {goog.asserts.AssertionError} When the value is not a function.
        195 */
        196goog.asserts.assertFunction = function(value, opt_message, var_args) {
        197 if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
        198 goog.asserts.doAssertFailure_('Expected function but got %s: %s.',
        199 [goog.typeOf(value), value], opt_message,
        200 Array.prototype.slice.call(arguments, 2));
        201 }
        202 return /** @type {!Function} */ (value);
        203};
        204
        205
        206/**
        207 * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
        208 * @param {*} value The value to check.
        209 * @param {string=} opt_message Error message in case of failure.
        210 * @param {...*} var_args The items to substitute into the failure message.
        211 * @return {!Object} The value, guaranteed to be a non-null object.
        212 * @throws {goog.asserts.AssertionError} When the value is not an object.
        213 */
        214goog.asserts.assertObject = function(value, opt_message, var_args) {
        215 if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
        216 goog.asserts.doAssertFailure_('Expected object but got %s: %s.',
        217 [goog.typeOf(value), value],
        218 opt_message, Array.prototype.slice.call(arguments, 2));
        219 }
        220 return /** @type {!Object} */ (value);
        221};
        222
        223
        224/**
        225 * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
        226 * @param {*} value The value to check.
        227 * @param {string=} opt_message Error message in case of failure.
        228 * @param {...*} var_args The items to substitute into the failure message.
        229 * @return {!Array} The value, guaranteed to be a non-null array.
        230 * @throws {goog.asserts.AssertionError} When the value is not an array.
        231 */
        232goog.asserts.assertArray = function(value, opt_message, var_args) {
        233 if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) {
        234 goog.asserts.doAssertFailure_('Expected array but got %s: %s.',
        235 [goog.typeOf(value), value], opt_message,
        236 Array.prototype.slice.call(arguments, 2));
        237 }
        238 return /** @type {!Array} */ (value);
        239};
        240
        241
        242/**
        243 * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
        244 * @param {*} value The value to check.
        245 * @param {string=} opt_message Error message in case of failure.
        246 * @param {...*} var_args The items to substitute into the failure message.
        247 * @return {boolean} The value, guaranteed to be a boolean when asserts are
        248 * enabled.
        249 * @throws {goog.asserts.AssertionError} When the value is not a boolean.
        250 */
        251goog.asserts.assertBoolean = function(value, opt_message, var_args) {
        252 if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) {
        253 goog.asserts.doAssertFailure_('Expected boolean but got %s: %s.',
        254 [goog.typeOf(value), value], opt_message,
        255 Array.prototype.slice.call(arguments, 2));
        256 }
        257 return /** @type {boolean} */ (value);
        258};
        259
        260
        261/**
        262 * Checks if the value is an instance of the user-defined type if
        263 * goog.asserts.ENABLE_ASSERTS is true.
        264 *
        265 * The compiler may tighten the type returned by this function.
        266 *
        267 * @param {*} value The value to check.
        268 * @param {function(new: T, ...)} type A user-defined constructor.
        269 * @param {string=} opt_message Error message in case of failure.
        270 * @param {...*} var_args The items to substitute into the failure message.
        271 * @throws {goog.asserts.AssertionError} When the value is not an instance of
        272 * type.
        273 * @return {!T}
        274 * @template T
        275 */
        276goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
        277 if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
        278 goog.asserts.doAssertFailure_('instanceof check failed.', null,
        279 opt_message, Array.prototype.slice.call(arguments, 3));
        280 }
        281 return value;
        282};
        283
        \ No newline at end of file +asserts.js

        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 * @author agrieve@google.com (Andrew Grieve)
        35 */
        36
        37goog.provide('goog.asserts');
        38goog.provide('goog.asserts.AssertionError');
        39
        40goog.require('goog.debug.Error');
        41goog.require('goog.dom.NodeType');
        42goog.require('goog.string');
        43
        44
        45/**
        46 * @define {boolean} Whether to strip out asserts or to leave them in.
        47 */
        48goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);
        49
        50
        51
        52/**
        53 * Error object for failed assertions.
        54 * @param {string} messagePattern The pattern that was used to form message.
        55 * @param {!Array<*>} messageArgs The items to substitute into the pattern.
        56 * @constructor
        57 * @extends {goog.debug.Error}
        58 * @final
        59 */
        60goog.asserts.AssertionError = function(messagePattern, messageArgs) {
        61 messageArgs.unshift(messagePattern);
        62 goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
        63 // Remove the messagePattern afterwards to avoid permanently modifying the
        64 // passed in array.
        65 messageArgs.shift();
        66
        67 /**
        68 * The message pattern used to format the error message. Error handlers can
        69 * use this to uniquely identify the assertion.
        70 * @type {string}
        71 */
        72 this.messagePattern = messagePattern;
        73};
        74goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
        75
        76
        77/** @override */
        78goog.asserts.AssertionError.prototype.name = 'AssertionError';
        79
        80
        81/**
        82 * The default error handler.
        83 * @param {!goog.asserts.AssertionError} e The exception to be handled.
        84 */
        85goog.asserts.DEFAULT_ERROR_HANDLER = function(e) { throw e; };
        86
        87
        88/**
        89 * The handler responsible for throwing or logging assertion errors.
        90 * @private {function(!goog.asserts.AssertionError)}
        91 */
        92goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER;
        93
        94
        95/**
        96 * Throws an exception with the given message and "Assertion failed" prefixed
        97 * onto it.
        98 * @param {string} defaultMessage The message to use if givenMessage is empty.
        99 * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage.
        100 * @param {string|undefined} givenMessage Message supplied by the caller.
        101 * @param {Array<*>} givenArgs The substitution arguments for givenMessage.
        102 * @throws {goog.asserts.AssertionError} When the value is not a number.
        103 * @private
        104 */
        105goog.asserts.doAssertFailure_ =
        106 function(defaultMessage, defaultArgs, givenMessage, givenArgs) {
        107 var message = 'Assertion failed';
        108 if (givenMessage) {
        109 message += ': ' + givenMessage;
        110 var args = givenArgs;
        111 } else if (defaultMessage) {
        112 message += ': ' + defaultMessage;
        113 args = defaultArgs;
        114 }
        115 // The '' + works around an Opera 10 bug in the unit tests. Without it,
        116 // a stack trace is added to var message above. With this, a stack trace is
        117 // not added until this line (it causes the extra garbage to be added after
        118 // the assertion message instead of in the middle of it).
        119 var e = new goog.asserts.AssertionError('' + message, args || []);
        120 goog.asserts.errorHandler_(e);
        121};
        122
        123
        124/**
        125 * Sets a custom error handler that can be used to customize the behavior of
        126 * assertion failures, for example by turning all assertion failures into log
        127 * messages.
        128 * @param {function(!goog.asserts.AssertionError)} errorHandler
        129 */
        130goog.asserts.setErrorHandler = function(errorHandler) {
        131 if (goog.asserts.ENABLE_ASSERTS) {
        132 goog.asserts.errorHandler_ = errorHandler;
        133 }
        134};
        135
        136
        137/**
        138 * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
        139 * true.
        140 * @template T
        141 * @param {T} condition The condition to check.
        142 * @param {string=} opt_message Error message in case of failure.
        143 * @param {...*} var_args The items to substitute into the failure message.
        144 * @return {T} The value of the condition.
        145 * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
        146 */
        147goog.asserts.assert = function(condition, opt_message, var_args) {
        148 if (goog.asserts.ENABLE_ASSERTS && !condition) {
        149 goog.asserts.doAssertFailure_('', null, opt_message,
        150 Array.prototype.slice.call(arguments, 2));
        151 }
        152 return condition;
        153};
        154
        155
        156/**
        157 * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
        158 * when we want to add a check in the unreachable area like switch-case
        159 * statement:
        160 *
        161 * <pre>
        162 * switch(type) {
        163 * case FOO: doSomething(); break;
        164 * case BAR: doSomethingElse(); break;
        165 * default: goog.assert.fail('Unrecognized type: ' + type);
        166 * // We have only 2 types - "default:" section is unreachable code.
        167 * }
        168 * </pre>
        169 *
        170 * @param {string=} opt_message Error message in case of failure.
        171 * @param {...*} var_args The items to substitute into the failure message.
        172 * @throws {goog.asserts.AssertionError} Failure.
        173 */
        174goog.asserts.fail = function(opt_message, var_args) {
        175 if (goog.asserts.ENABLE_ASSERTS) {
        176 goog.asserts.errorHandler_(new goog.asserts.AssertionError(
        177 'Failure' + (opt_message ? ': ' + opt_message : ''),
        178 Array.prototype.slice.call(arguments, 1)));
        179 }
        180};
        181
        182
        183/**
        184 * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
        185 * @param {*} value The value to check.
        186 * @param {string=} opt_message Error message in case of failure.
        187 * @param {...*} var_args The items to substitute into the failure message.
        188 * @return {number} The value, guaranteed to be a number when asserts enabled.
        189 * @throws {goog.asserts.AssertionError} When the value is not a number.
        190 */
        191goog.asserts.assertNumber = function(value, opt_message, var_args) {
        192 if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) {
        193 goog.asserts.doAssertFailure_('Expected number but got %s: %s.',
        194 [goog.typeOf(value), value], opt_message,
        195 Array.prototype.slice.call(arguments, 2));
        196 }
        197 return /** @type {number} */ (value);
        198};
        199
        200
        201/**
        202 * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
        203 * @param {*} value The value to check.
        204 * @param {string=} opt_message Error message in case of failure.
        205 * @param {...*} var_args The items to substitute into the failure message.
        206 * @return {string} The value, guaranteed to be a string when asserts enabled.
        207 * @throws {goog.asserts.AssertionError} When the value is not a string.
        208 */
        209goog.asserts.assertString = function(value, opt_message, var_args) {
        210 if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) {
        211 goog.asserts.doAssertFailure_('Expected string but got %s: %s.',
        212 [goog.typeOf(value), value], opt_message,
        213 Array.prototype.slice.call(arguments, 2));
        214 }
        215 return /** @type {string} */ (value);
        216};
        217
        218
        219/**
        220 * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
        221 * @param {*} value The value to check.
        222 * @param {string=} opt_message Error message in case of failure.
        223 * @param {...*} var_args The items to substitute into the failure message.
        224 * @return {!Function} The value, guaranteed to be a function when asserts
        225 * enabled.
        226 * @throws {goog.asserts.AssertionError} When the value is not a function.
        227 */
        228goog.asserts.assertFunction = function(value, opt_message, var_args) {
        229 if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
        230 goog.asserts.doAssertFailure_('Expected function but got %s: %s.',
        231 [goog.typeOf(value), value], opt_message,
        232 Array.prototype.slice.call(arguments, 2));
        233 }
        234 return /** @type {!Function} */ (value);
        235};
        236
        237
        238/**
        239 * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
        240 * @param {*} value The value to check.
        241 * @param {string=} opt_message Error message in case of failure.
        242 * @param {...*} var_args The items to substitute into the failure message.
        243 * @return {!Object} The value, guaranteed to be a non-null object.
        244 * @throws {goog.asserts.AssertionError} When the value is not an object.
        245 */
        246goog.asserts.assertObject = function(value, opt_message, var_args) {
        247 if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
        248 goog.asserts.doAssertFailure_('Expected object but got %s: %s.',
        249 [goog.typeOf(value), value],
        250 opt_message, Array.prototype.slice.call(arguments, 2));
        251 }
        252 return /** @type {!Object} */ (value);
        253};
        254
        255
        256/**
        257 * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
        258 * @param {*} value The value to check.
        259 * @param {string=} opt_message Error message in case of failure.
        260 * @param {...*} var_args The items to substitute into the failure message.
        261 * @return {!Array<?>} The value, guaranteed to be a non-null array.
        262 * @throws {goog.asserts.AssertionError} When the value is not an array.
        263 */
        264goog.asserts.assertArray = function(value, opt_message, var_args) {
        265 if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) {
        266 goog.asserts.doAssertFailure_('Expected array but got %s: %s.',
        267 [goog.typeOf(value), value], opt_message,
        268 Array.prototype.slice.call(arguments, 2));
        269 }
        270 return /** @type {!Array<?>} */ (value);
        271};
        272
        273
        274/**
        275 * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
        276 * @param {*} value The value to check.
        277 * @param {string=} opt_message Error message in case of failure.
        278 * @param {...*} var_args The items to substitute into the failure message.
        279 * @return {boolean} The value, guaranteed to be a boolean when asserts are
        280 * enabled.
        281 * @throws {goog.asserts.AssertionError} When the value is not a boolean.
        282 */
        283goog.asserts.assertBoolean = function(value, opt_message, var_args) {
        284 if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) {
        285 goog.asserts.doAssertFailure_('Expected boolean but got %s: %s.',
        286 [goog.typeOf(value), value], opt_message,
        287 Array.prototype.slice.call(arguments, 2));
        288 }
        289 return /** @type {boolean} */ (value);
        290};
        291
        292
        293/**
        294 * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.
        295 * @param {*} value The value to check.
        296 * @param {string=} opt_message Error message in case of failure.
        297 * @param {...*} var_args The items to substitute into the failure message.
        298 * @return {!Element} The value, likely to be a DOM Element when asserts are
        299 * enabled.
        300 * @throws {goog.asserts.AssertionError} When the value is not an Element.
        301 */
        302goog.asserts.assertElement = function(value, opt_message, var_args) {
        303 if (goog.asserts.ENABLE_ASSERTS && (!goog.isObject(value) ||
        304 value.nodeType != goog.dom.NodeType.ELEMENT)) {
        305 goog.asserts.doAssertFailure_('Expected Element but got %s: %s.',
        306 [goog.typeOf(value), value], opt_message,
        307 Array.prototype.slice.call(arguments, 2));
        308 }
        309 return /** @type {!Element} */ (value);
        310};
        311
        312
        313/**
        314 * Checks if the value is an instance of the user-defined type if
        315 * goog.asserts.ENABLE_ASSERTS is true.
        316 *
        317 * The compiler may tighten the type returned by this function.
        318 *
        319 * @param {?} value The value to check.
        320 * @param {function(new: T, ...)} type A user-defined constructor.
        321 * @param {string=} opt_message Error message in case of failure.
        322 * @param {...*} var_args The items to substitute into the failure message.
        323 * @throws {goog.asserts.AssertionError} When the value is not an instance of
        324 * type.
        325 * @return {T}
        326 * @template T
        327 */
        328goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
        329 if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
        330 goog.asserts.doAssertFailure_('Expected instanceof %s but got %s.',
        331 [goog.asserts.getType_(type), goog.asserts.getType_(value)],
        332 opt_message, Array.prototype.slice.call(arguments, 3));
        333 }
        334 return value;
        335};
        336
        337
        338/**
        339 * Checks that no enumerable keys are present in Object.prototype. Such keys
        340 * would break most code that use {@code for (var ... in ...)} loops.
        341 */
        342goog.asserts.assertObjectPrototypeIsIntact = function() {
        343 for (var key in Object.prototype) {
        344 goog.asserts.fail(key + ' should not be enumerable in Object.prototype.');
        345 }
        346};
        347
        348
        349/**
        350 * Returns the type of a value. If a constructor is passed, and a suitable
        351 * string cannot be found, 'unknown type name' will be returned.
        352 * @param {*} value A constructor, object, or primitive.
        353 * @return {string} The best display name for the value, or 'unknown type name'.
        354 * @private
        355 */
        356goog.asserts.getType_ = function(value) {
        357 if (value instanceof Function) {
        358 return value.displayName || value.name || 'unknown type name';
        359 } else if (value instanceof Object) {
        360 return value.constructor.displayName || value.constructor.name ||
        361 Object.prototype.toString.call(value);
        362 } else {
        363 return value === null ? 'null' : typeof value;
        364 }
        365};
        \ No newline at end of file diff --git a/docs/source/lib/goog/async/freelist.js.src.html b/docs/source/lib/goog/async/freelist.js.src.html new file mode 100644 index 0000000..6c76c1c --- /dev/null +++ b/docs/source/lib/goog/async/freelist.js.src.html @@ -0,0 +1 @@ +freelist.js

        lib/goog/async/freelist.js

        1// Copyright 2015 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 freelist.
        17 *
        18 * An anterative to goog.structs.SimplePool, it imposes the requirement that the
        19 * objects in the list contain a "next" property that can be used to maintain
        20 * the pool.
        21 */
        22
        23goog.provide('goog.async.FreeList');
        24
        25
        26/**
        27 * @template ITEM
        28 */
        29goog.async.FreeList = goog.defineClass(null, {
        30 /**
        31 * @param {function():ITEM} create
        32 * @param {function(ITEM):void} reset
        33 * @param {number} limit
        34 */
        35 constructor: function(create, reset, limit) {
        36 /** @const {number} */
        37 this.limit_ = limit;
        38 /** @const {function()} */
        39 this.create_ = create;
        40 /** @const {function(ITEM):void} */
        41 this.reset_ = reset;
        42
        43 /** @type {number} */
        44 this.occupants_ = 0;
        45 /** @type {ITEM} */
        46 this.head_ = null;
        47 },
        48
        49 /**
        50 * @return {ITEM}
        51 */
        52 get: function() {
        53 var item;
        54 if (this.occupants_ > 0) {
        55 this.occupants_--;
        56 item = this.head_;
        57 this.head_ = item.next;
        58 item.next = null;
        59 } else {
        60 item = this.create_();
        61 }
        62 return item;
        63 },
        64
        65 /**
        66 * @param {ITEM} item An item available for possible future reuse.
        67 */
        68 put: function(item) {
        69 this.reset_(item);
        70 if (this.occupants_ < this.limit_) {
        71 this.occupants_++;
        72 item.next = this.head_;
        73 this.head_ = item;
        74 }
        75 },
        76
        77 /**
        78 * Visible for testing.
        79 * @package
        80 * @return {number}
        81 */
        82 occupants: function() {
        83 return this.occupants_;
        84 }
        85});
        86
        87
        88
        \ No newline at end of file diff --git a/docs/source/lib/goog/async/nexttick.js.src.html b/docs/source/lib/goog/async/nexttick.js.src.html new file mode 100644 index 0000000..c1b8f1d --- /dev/null +++ b/docs/source/lib/goog/async/nexttick.js.src.html @@ -0,0 +1 @@ +nexttick.js

        lib/goog/async/nexttick.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 Provides a function to schedule running a function as soon
        17 * as possible after the current JS execution stops and yields to the event
        18 * loop.
        19 *
        20 */
        21
        22goog.provide('goog.async.nextTick');
        23goog.provide('goog.async.throwException');
        24
        25goog.require('goog.debug.entryPointRegistry');
        26goog.require('goog.dom.TagName');
        27goog.require('goog.functions');
        28goog.require('goog.labs.userAgent.browser');
        29goog.require('goog.labs.userAgent.engine');
        30
        31
        32/**
        33 * Throw an item without interrupting the current execution context. For
        34 * example, if processing a group of items in a loop, sometimes it is useful
        35 * to report an error while still allowing the rest of the batch to be
        36 * processed.
        37 * @param {*} exception
        38 */
        39goog.async.throwException = function(exception) {
        40 // Each throw needs to be in its own context.
        41 goog.global.setTimeout(function() { throw exception; }, 0);
        42};
        43
        44
        45/**
        46 * Fires the provided callbacks as soon as possible after the current JS
        47 * execution context. setTimeout(…, 0) takes at least 4ms when called from
        48 * within another setTimeout(…, 0) for legacy reasons.
        49 *
        50 * This will not schedule the callback as a microtask (i.e. a task that can
        51 * preempt user input or networking callbacks). It is meant to emulate what
        52 * setTimeout(_, 0) would do if it were not throttled. If you desire microtask
        53 * behavior, use {@see goog.Promise} instead.
        54 *
        55 * @param {function(this:SCOPE)} callback Callback function to fire as soon as
        56 * possible.
        57 * @param {SCOPE=} opt_context Object in whose scope to call the listener.
        58 * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that
        59 * ensures correctness at the cost of speed. See comments for details.
        60 * @template SCOPE
        61 */
        62goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) {
        63 var cb = callback;
        64 if (opt_context) {
        65 cb = goog.bind(callback, opt_context);
        66 }
        67 cb = goog.async.nextTick.wrapCallback_(cb);
        68 // window.setImmediate was introduced and currently only supported by IE10+,
        69 // but due to a bug in the implementation it is not guaranteed that
        70 // setImmediate is faster than setTimeout nor that setImmediate N is before
        71 // setImmediate N+1. That is why we do not use the native version if
        72 // available. We do, however, call setImmediate if it is a normal function
        73 // because that indicates that it has been replaced by goog.testing.MockClock
        74 // which we do want to support.
        75 // See
        76 // http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10
        77 //
        78 // Note we do allow callers to also request setImmediate if they are willing
        79 // to accept the possible tradeoffs of incorrectness in exchange for speed.
        80 // The IE fallback of readystate change is much slower.
        81 if (goog.isFunction(goog.global.setImmediate) &&
        82 // Opt in.
        83 (opt_useSetImmediate ||
        84 // or it isn't a browser or the environment is weird
        85 !goog.global.Window || !goog.global.Window.prototype ||
        86 // or something redefined setImmediate in which case we (YOLO) decide
        87 // to use it (This is so that we use the mockClock setImmediate. sigh).
        88 goog.global.Window.prototype.setImmediate != goog.global.setImmediate)) {
        89 goog.global.setImmediate(cb);
        90 return;
        91 }
        92
        93 // Look for and cache the custom fallback version of setImmediate.
        94 if (!goog.async.nextTick.setImmediate_) {
        95 goog.async.nextTick.setImmediate_ =
        96 goog.async.nextTick.getSetImmediateEmulator_();
        97 }
        98 goog.async.nextTick.setImmediate_(cb);
        99};
        100
        101
        102/**
        103 * Cache for the setImmediate implementation.
        104 * @type {function(function())}
        105 * @private
        106 */
        107goog.async.nextTick.setImmediate_;
        108
        109
        110/**
        111 * Determines the best possible implementation to run a function as soon as
        112 * the JS event loop is idle.
        113 * @return {function(function())} The "setImmediate" implementation.
        114 * @private
        115 */
        116goog.async.nextTick.getSetImmediateEmulator_ = function() {
        117 // Create a private message channel and use it to postMessage empty messages
        118 // to ourselves.
        119 var Channel = goog.global['MessageChannel'];
        120 // If MessageChannel is not available and we are in a browser, implement
        121 // an iframe based polyfill in browsers that have postMessage and
        122 // document.addEventListener. The latter excludes IE8 because it has a
        123 // synchronous postMessage implementation.
        124 if (typeof Channel === 'undefined' && typeof window !== 'undefined' &&
        125 window.postMessage && window.addEventListener &&
        126 // Presto (The old pre-blink Opera engine) has problems with iframes
        127 // and contentWindow.
        128 !goog.labs.userAgent.engine.isPresto()) {
        129 /** @constructor */
        130 Channel = function() {
        131 // Make an empty, invisible iframe.
        132 var iframe = document.createElement(goog.dom.TagName.IFRAME);
        133 iframe.style.display = 'none';
        134 iframe.src = '';
        135 document.documentElement.appendChild(iframe);
        136 var win = iframe.contentWindow;
        137 var doc = win.document;
        138 doc.open();
        139 doc.write('');
        140 doc.close();
        141 // Do not post anything sensitive over this channel, as the workaround for
        142 // pages with file: origin could allow that information to be modified or
        143 // intercepted.
        144 var message = 'callImmediate' + Math.random();
        145 // The same origin policy rejects attempts to postMessage from file: urls
        146 // unless the origin is '*'.
        147 // TODO(b/16335441): Use '*' origin for data: and other similar protocols.
        148 var origin = win.location.protocol == 'file:' ?
        149 '*' : win.location.protocol + '//' + win.location.host;
        150 var onmessage = goog.bind(function(e) {
        151 // Validate origin and message to make sure that this message was
        152 // intended for us. If the origin is set to '*' (see above) only the
        153 // message needs to match since, for example, '*' != 'file://'. Allowing
        154 // the wildcard is ok, as we are not concerned with security here.
        155 if ((origin != '*' && e.origin != origin) || e.data != message) {
        156 return;
        157 }
        158 this['port1'].onmessage();
        159 }, this);
        160 win.addEventListener('message', onmessage, false);
        161 this['port1'] = {};
        162 this['port2'] = {
        163 postMessage: function() {
        164 win.postMessage(message, origin);
        165 }
        166 };
        167 };
        168 }
        169 if (typeof Channel !== 'undefined' &&
        170 (!goog.labs.userAgent.browser.isIE())) {
        171 // Exclude all of IE due to
        172 // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/
        173 // which allows starving postMessage with a busy setTimeout loop.
        174 // This currently affects IE10 and IE11 which would otherwise be able
        175 // to use the postMessage based fallbacks.
        176 var channel = new Channel();
        177 // Use a fifo linked list to call callbacks in the right order.
        178 var head = {};
        179 var tail = head;
        180 channel['port1'].onmessage = function() {
        181 if (goog.isDef(head.next)) {
        182 head = head.next;
        183 var cb = head.cb;
        184 head.cb = null;
        185 cb();
        186 }
        187 };
        188 return function(cb) {
        189 tail.next = {
        190 cb: cb
        191 };
        192 tail = tail.next;
        193 channel['port2'].postMessage(0);
        194 };
        195 }
        196 // Implementation for IE6+: Script elements fire an asynchronous
        197 // onreadystatechange event when inserted into the DOM.
        198 if (typeof document !== 'undefined' && 'onreadystatechange' in
        199 document.createElement(goog.dom.TagName.SCRIPT)) {
        200 return function(cb) {
        201 var script = document.createElement(goog.dom.TagName.SCRIPT);
        202 script.onreadystatechange = function() {
        203 // Clean up and call the callback.
        204 script.onreadystatechange = null;
        205 script.parentNode.removeChild(script);
        206 script = null;
        207 cb();
        208 cb = null;
        209 };
        210 document.documentElement.appendChild(script);
        211 };
        212 }
        213 // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms
        214 // or more.
        215 return function(cb) {
        216 goog.global.setTimeout(cb, 0);
        217 };
        218};
        219
        220
        221/**
        222 * Helper function that is overrided to protect callbacks with entry point
        223 * monitor if the application monitors entry points.
        224 * @param {function()} callback Callback function to fire as soon as possible.
        225 * @return {function()} The wrapped callback.
        226 * @private
        227 */
        228goog.async.nextTick.wrapCallback_ = goog.functions.identity;
        229
        230
        231// Register the callback function as an entry point, so that it can be
        232// monitored for exception handling, etc. This has to be done in this file
        233// since it requires special code to handle all browsers.
        234goog.debug.entryPointRegistry.register(
        235 /**
        236 * @param {function(!Function): !Function} transformer The transforming
        237 * function.
        238 */
        239 function(transformer) {
        240 goog.async.nextTick.wrapCallback_ = transformer;
        241 });
        \ No newline at end of file diff --git a/docs/source/lib/goog/async/run.js.src.html b/docs/source/lib/goog/async/run.js.src.html new file mode 100644 index 0000000..ce3194f --- /dev/null +++ b/docs/source/lib/goog/async/run.js.src.html @@ -0,0 +1 @@ +run.js

        lib/goog/async/run.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
        15goog.provide('goog.async.run');
        16
        17goog.require('goog.async.WorkQueue');
        18goog.require('goog.async.nextTick');
        19goog.require('goog.async.throwException');
        20
        21
        22/**
        23 * Fires the provided callback just before the current callstack unwinds, or as
        24 * soon as possible after the current JS execution context.
        25 * @param {function(this:THIS)} callback
        26 * @param {THIS=} opt_context Object to use as the "this value" when calling
        27 * the provided function.
        28 * @template THIS
        29 */
        30goog.async.run = function(callback, opt_context) {
        31 if (!goog.async.run.schedule_) {
        32 goog.async.run.initializeRunner_();
        33 }
        34 if (!goog.async.run.workQueueScheduled_) {
        35 // Nothing is currently scheduled, schedule it now.
        36 goog.async.run.schedule_();
        37 goog.async.run.workQueueScheduled_ = true;
        38 }
        39
        40 goog.async.run.workQueue_.add(callback, opt_context);
        41};
        42
        43
        44/**
        45 * Initializes the function to use to process the work queue.
        46 * @private
        47 */
        48goog.async.run.initializeRunner_ = function() {
        49 // If native Promises are available in the browser, just schedule the callback
        50 // on a fulfilled promise, which is specified to be async, but as fast as
        51 // possible.
        52 if (goog.global.Promise && goog.global.Promise.resolve) {
        53 var promise = goog.global.Promise.resolve(undefined);
        54 goog.async.run.schedule_ = function() {
        55 promise.then(goog.async.run.processWorkQueue);
        56 };
        57 } else {
        58 goog.async.run.schedule_ = function() {
        59 goog.async.nextTick(goog.async.run.processWorkQueue);
        60 };
        61 }
        62};
        63
        64
        65/**
        66 * Forces goog.async.run to use nextTick instead of Promise.
        67 *
        68 * This should only be done in unit tests. It's useful because MockClock
        69 * replaces nextTick, but not the browser Promise implementation, so it allows
        70 * Promise-based code to be tested with MockClock.
        71 *
        72 * However, we also want to run promises if the MockClock is no longer in
        73 * control so we schedule a backup "setTimeout" to the unmocked timeout if
        74 * provided.
        75 *
        76 * @param {function(function())=} opt_realSetTimeout
        77 */
        78goog.async.run.forceNextTick = function(opt_realSetTimeout) {
        79 goog.async.run.schedule_ = function() {
        80 goog.async.nextTick(goog.async.run.processWorkQueue);
        81 if (opt_realSetTimeout) {
        82 opt_realSetTimeout(goog.async.run.processWorkQueue);
        83 }
        84 };
        85};
        86
        87
        88/**
        89 * The function used to schedule work asynchronousely.
        90 * @private {function()}
        91 */
        92goog.async.run.schedule_;
        93
        94
        95/** @private {boolean} */
        96goog.async.run.workQueueScheduled_ = false;
        97
        98
        99/** @private {!goog.async.WorkQueue} */
        100goog.async.run.workQueue_ = new goog.async.WorkQueue();
        101
        102
        103if (goog.DEBUG) {
        104 /**
        105 * Reset the work queue. Only available for tests in debug mode.
        106 */
        107 goog.async.run.resetQueue = function() {
        108 goog.async.run.workQueueScheduled_ = false;
        109 goog.async.run.workQueue_ = new goog.async.WorkQueue();
        110 };
        111}
        112
        113
        114/**
        115 * Run any pending goog.async.run work items. This function is not intended
        116 * for general use, but for use by entry point handlers to run items ahead of
        117 * goog.async.nextTick.
        118 */
        119goog.async.run.processWorkQueue = function() {
        120 // NOTE: additional work queue items may be added while processing.
        121 var item = null;
        122 while (item = goog.async.run.workQueue_.remove()) {
        123 try {
        124 item.fn.call(item.scope);
        125 } catch (e) {
        126 goog.async.throwException(e);
        127 }
        128 goog.async.run.workQueue_.returnUnused(item);
        129 }
        130
        131 // There are no more work items, allow processing to be scheduled again.
        132 goog.async.run.workQueueScheduled_ = false;
        133};
        \ No newline at end of file diff --git a/docs/source/lib/goog/async/workqueue.js.src.html b/docs/source/lib/goog/async/workqueue.js.src.html new file mode 100644 index 0000000..bc75c92 --- /dev/null +++ b/docs/source/lib/goog/async/workqueue.js.src.html @@ -0,0 +1 @@ +workqueue.js

        lib/goog/async/workqueue.js

        1// Copyright 2015 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
        15goog.provide('goog.async.WorkItem');
        16goog.provide('goog.async.WorkQueue');
        17
        18goog.require('goog.asserts');
        19goog.require('goog.async.FreeList');
        20
        21
        22// TODO(johnlenz): generalize the WorkQueue if this is used by more
        23// than goog.async.run.
        24
        25
        26
        27/**
        28 * A low GC workqueue. The key elements of this design:
        29 * - avoids the need for goog.bind or equivalent by carrying scope
        30 * - avoids the need for array reallocation by using a linked list
        31 * - minimizes work entry objects allocation by recycling objects
        32 * @constructor
        33 * @final
        34 * @struct
        35 */
        36goog.async.WorkQueue = function() {
        37 this.workHead_ = null;
        38 this.workTail_ = null;
        39};
        40
        41
        42/** @define {number} The maximum number of entries to keep for recycling. */
        43goog.define('goog.async.WorkQueue.DEFAULT_MAX_UNUSED', 100);
        44
        45
        46/** @const @private {goog.async.FreeList<goog.async.WorkItem>} */
        47goog.async.WorkQueue.freelist_ = new goog.async.FreeList(
        48 function() {return new goog.async.WorkItem(); },
        49 function(item) {item.reset()},
        50 goog.async.WorkQueue.DEFAULT_MAX_UNUSED);
        51
        52
        53/**
        54 * @param {function()} fn
        55 * @param {Object|null|undefined} scope
        56 */
        57goog.async.WorkQueue.prototype.add = function(fn, scope) {
        58 var item = this.getUnusedItem_();
        59 item.set(fn, scope);
        60
        61 if (this.workTail_) {
        62 this.workTail_.next = item;
        63 this.workTail_ = item;
        64 } else {
        65 goog.asserts.assert(!this.workHead_);
        66 this.workHead_ = item;
        67 this.workTail_ = item;
        68 }
        69};
        70
        71
        72/**
        73 * @return {goog.async.WorkItem}
        74 */
        75goog.async.WorkQueue.prototype.remove = function() {
        76 var item = null;
        77
        78 if (this.workHead_) {
        79 item = this.workHead_;
        80 this.workHead_ = this.workHead_.next;
        81 if (!this.workHead_) {
        82 this.workTail_ = null;
        83 }
        84 item.next = null;
        85 }
        86 return item;
        87};
        88
        89
        90/**
        91 * @param {goog.async.WorkItem} item
        92 */
        93goog.async.WorkQueue.prototype.returnUnused = function(item) {
        94 goog.async.WorkQueue.freelist_.put(item);
        95};
        96
        97
        98/**
        99 * @return {goog.async.WorkItem}
        100 * @private
        101 */
        102goog.async.WorkQueue.prototype.getUnusedItem_ = function() {
        103 return goog.async.WorkQueue.freelist_.get();
        104};
        105
        106
        107
        108/**
        109 * @constructor
        110 * @final
        111 * @struct
        112 */
        113goog.async.WorkItem = function() {
        114 /** @type {?function()} */
        115 this.fn = null;
        116 /** @type {Object|null|undefined} */
        117 this.scope = null;
        118 /** @type {?goog.async.WorkItem} */
        119 this.next = null;
        120};
        121
        122
        123/**
        124 * @param {function()} fn
        125 * @param {Object|null|undefined} scope
        126 */
        127goog.async.WorkItem.prototype.set = function(fn, scope) {
        128 this.fn = fn;
        129 this.scope = scope;
        130 this.next = null;
        131};
        132
        133
        134/** Reset the work item so they don't prevent GC before reuse */
        135goog.async.WorkItem.prototype.reset = function() {
        136 this.fn = null;
        137 this.scope = null;
        138 this.next = null;
        139};
        \ No newline at end of file diff --git a/docs/source/lib/goog/base.js.src.html b/docs/source/lib/goog/base.js.src.html index dd4e1b2..98c0c08 100644 --- a/docs/source/lib/goog/base.js.src.html +++ b/docs/source/lib/goog/base.js.src.html @@ -1 +1 @@ -base.js

        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 */
        31var COMPILED = false;
        32
        33
        34/**
        35 * Base namespace for the Closure library. Checks to see goog is
        36 * already defined in the current scope before assigning to prevent
        37 * clobbering if base.js is loaded more than once.
        38 *
        39 * @const
        40 */
        41var goog = goog || {};
        42
        43
        44/**
        45 * Reference to the global context. In most cases this will be 'window'.
        46 */
        47goog.global = this;
        48
        49
        50/**
        51 * A hook for overriding the define values in uncompiled mode.
        52 *
        53 * In uncompiled mode, {@code CLOSURE_DEFINES} may be defined before loading
        54 * base.js. If a key is defined in {@code CLOSURE_DEFINES}, {@code goog.define}
        55 * will use the value instead of the default value. This allows flags to be
        56 * overwritten without compilation (this is normally accomplished with the
        57 * compiler's "define" flag).
        58 *
        59 * Example:
        60 * <pre>
        61 * var CLOSURE_DEFINES = {'goog.DEBUG', false};
        62 * </pre>
        63 *
        64 * @type {Object.<string, (string|number|boolean)>|undefined}
        65 */
        66goog.global.CLOSURE_DEFINES;
        67
        68
        69/**
        70 * Builds an object structure for the provided namespace path,
        71 * ensuring that names that already exist are not overwritten. For
        72 * example:
        73 * "a.b.c" -> a = {};a.b={};a.b.c={};
        74 * Used by goog.provide and goog.exportSymbol.
        75 * @param {string} name name of the object that this file defines.
        76 * @param {*=} opt_object the object to expose at the end of the path.
        77 * @param {Object=} opt_objectToExportTo The object to add the path to; default
        78 * is |goog.global|.
        79 * @private
        80 */
        81goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
        82 var parts = name.split('.');
        83 var cur = opt_objectToExportTo || goog.global;
        84
        85 // Internet Explorer exhibits strange behavior when throwing errors from
        86 // methods externed in this manner. See the testExportSymbolExceptions in
        87 // base_test.html for an example.
        88 if (!(parts[0] in cur) && cur.execScript) {
        89 cur.execScript('var ' + parts[0]);
        90 }
        91
        92 // Certain browsers cannot parse code in the form for((a in b); c;);
        93 // This pattern is produced by the JSCompiler when it collapses the
        94 // statement above into the conditional loop below. To prevent this from
        95 // happening, use a for-loop and reserve the init logic as below.
        96
        97 // Parentheses added to eliminate strict JS warning in Firefox.
        98 for (var part; parts.length && (part = parts.shift());) {
        99 if (!parts.length && opt_object !== undefined) {
        100 // last part and we have an object; use it
        101 cur[part] = opt_object;
        102 } else if (cur[part]) {
        103 cur = cur[part];
        104 } else {
        105 cur = cur[part] = {};
        106 }
        107 }
        108};
        109
        110
        111/**
        112 * Defines a named value. In uncompiled mode, the value is retreived from
        113 * CLOSURE_DEFINES if the object is defined and has the property specified,
        114 * and otherwise used the defined defaultValue. When compiled, the default
        115 * can be overridden using compiler command-line options.
        116 *
        117 * @param {string} name The distinguished name to provide.
        118 * @param {string|number|boolean} defaultValue
        119 */
        120goog.define = function(name, defaultValue) {
        121 var value = defaultValue;
        122 if (!COMPILED) {
        123 if (goog.global.CLOSURE_DEFINES && Object.prototype.hasOwnProperty.call(
        124 goog.global.CLOSURE_DEFINES, name)) {
        125 value = goog.global.CLOSURE_DEFINES[name];
        126 }
        127 }
        128 goog.exportPath_(name, value);
        129};
        130
        131
        132/**
        133 * @define {boolean} DEBUG is provided as a convenience so that debugging code
        134 * that should not be included in a production js_binary can be easily stripped
        135 * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
        136 * toString() methods should be declared inside an "if (goog.DEBUG)" conditional
        137 * because they are generally used for debugging purposes and it is difficult
        138 * for the JSCompiler to statically determine whether they are used.
        139 */
        140goog.DEBUG = true;
        141
        142
        143/**
        144 * @define {string} LOCALE defines the locale being used for compilation. It is
        145 * used to select locale specific data to be compiled in js binary. BUILD rule
        146 * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler
        147 * option.
        148 *
        149 * Take into account that the locale code format is important. You should use
        150 * the canonical Unicode format with hyphen as a delimiter. Language must be
        151 * lowercase, Language Script - Capitalized, Region - UPPERCASE.
        152 * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
        153 *
        154 * See more info about locale codes here:
        155 * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
        156 *
        157 * For language codes you should use values defined by ISO 693-1. See it here
        158 * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
        159 * this rule: the Hebrew language. For legacy reasons the old code (iw) should
        160 * be used instead of the new code (he), see http://wiki/Main/IIISynonyms.
        161 */
        162goog.define('goog.LOCALE', 'en'); // default to en
        163
        164
        165/**
        166 * @define {boolean} Whether this code is running on trusted sites.
        167 *
        168 * On untrusted sites, several native functions can be defined or overridden by
        169 * external libraries like Prototype, Datejs, and JQuery and setting this flag
        170 * to false forces closure to use its own implementations when possible.
        171 *
        172 * If your javascript can be loaded by a third party site and you are wary about
        173 * relying on non-standard implementations, specify
        174 * "--define goog.TRUSTED_SITE=false" to the JSCompiler.
        175 */
        176goog.define('goog.TRUSTED_SITE', true);
        177
        178
        179/**
        180 * Creates object stubs for a namespace. The presence of one or more
        181 * goog.provide() calls indicate that the file defines the given
        182 * objects/namespaces. Build tools also scan for provide/require statements
        183 * to discern dependencies, build dependency files (see deps.js), etc.
        184 * @see goog.require
        185 * @param {string} name Namespace provided by this file in the form
        186 * "goog.package.part".
        187 */
        188goog.provide = function(name) {
        189 if (!COMPILED) {
        190 // Ensure that the same namespace isn't provided twice. This is intended
        191 // to teach new developers that 'goog.provide' is effectively a variable
        192 // declaration. And when JSCompiler transforms goog.provide into a real
        193 // variable declaration, the compiled JS should work the same as the raw
        194 // JS--even when the raw JS uses goog.provide incorrectly.
        195 if (goog.isProvided_(name)) {
        196 throw Error('Namespace "' + name + '" already declared.');
        197 }
        198 delete goog.implicitNamespaces_[name];
        199
        200 var namespace = name;
        201 while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
        202 if (goog.getObjectByName(namespace)) {
        203 break;
        204 }
        205 goog.implicitNamespaces_[namespace] = true;
        206 }
        207 }
        208
        209 goog.exportPath_(name);
        210};
        211
        212
        213/**
        214 * Marks that the current file should only be used for testing, and never for
        215 * live code in production.
        216 *
        217 * In the case of unit tests, the message may optionally be an exact
        218 * namespace for the test (e.g. 'goog.stringTest'). The linter will then
        219 * ignore the extra provide (if not explicitly defined in the code).
        220 *
        221 * @param {string=} opt_message Optional message to add to the error that's
        222 * raised when used in production code.
        223 */
        224goog.setTestOnly = function(opt_message) {
        225 if (COMPILED && !goog.DEBUG) {
        226 opt_message = opt_message || '';
        227 throw Error('Importing test-only code into non-debug environment' +
        228 opt_message ? ': ' + opt_message : '.');
        229 }
        230};
        231
        232
        233if (!COMPILED) {
        234
        235 /**
        236 * Check if the given name has been goog.provided. This will return false for
        237 * names that are available only as implicit namespaces.
        238 * @param {string} name name of the object to look for.
        239 * @return {boolean} Whether the name has been provided.
        240 * @private
        241 */
        242 goog.isProvided_ = function(name) {
        243 return !goog.implicitNamespaces_[name] && !!goog.getObjectByName(name);
        244 };
        245
        246 /**
        247 * Namespaces implicitly defined by goog.provide. For example,
        248 * goog.provide('goog.events.Event') implicitly declares
        249 * that 'goog' and 'goog.events' must be namespaces.
        250 *
        251 * @type {Object}
        252 * @private
        253 */
        254 goog.implicitNamespaces_ = {};
        255}
        256
        257
        258/**
        259 * Returns an object based on its fully qualified external name. If you are
        260 * using a compilation pass that renames property names beware that using this
        261 * function will not find renamed properties.
        262 *
        263 * @param {string} name The fully qualified name.
        264 * @param {Object=} opt_obj The object within which to look; default is
        265 * |goog.global|.
        266 * @return {?} The value (object or primitive) or, if not found, null.
        267 */
        268goog.getObjectByName = function(name, opt_obj) {
        269 var parts = name.split('.');
        270 var cur = opt_obj || goog.global;
        271 for (var part; part = parts.shift(); ) {
        272 if (goog.isDefAndNotNull(cur[part])) {
        273 cur = cur[part];
        274 } else {
        275 return null;
        276 }
        277 }
        278 return cur;
        279};
        280
        281
        282/**
        283 * Globalizes a whole namespace, such as goog or goog.lang.
        284 *
        285 * @param {Object} obj The namespace to globalize.
        286 * @param {Object=} opt_global The object to add the properties to.
        287 * @deprecated Properties may be explicitly exported to the global scope, but
        288 * this should no longer be done in bulk.
        289 */
        290goog.globalize = function(obj, opt_global) {
        291 var global = opt_global || goog.global;
        292 for (var x in obj) {
        293 global[x] = obj[x];
        294 }
        295};
        296
        297
        298/**
        299 * Adds a dependency from a file to the files it requires.
        300 * @param {string} relPath The path to the js file.
        301 * @param {Array} provides An array of strings with the names of the objects
        302 * this file provides.
        303 * @param {Array} requires An array of strings with the names of the objects
        304 * this file requires.
        305 */
        306goog.addDependency = function(relPath, provides, requires) {
        307 if (goog.DEPENDENCIES_ENABLED) {
        308 var provide, require;
        309 var path = relPath.replace(/\\/g, '/');
        310 var deps = goog.dependencies_;
        311 for (var i = 0; provide = provides[i]; i++) {
        312 deps.nameToPath[provide] = path;
        313 if (!(path in deps.pathToNames)) {
        314 deps.pathToNames[path] = {};
        315 }
        316 deps.pathToNames[path][provide] = true;
        317 }
        318 for (var j = 0; require = requires[j]; j++) {
        319 if (!(path in deps.requires)) {
        320 deps.requires[path] = {};
        321 }
        322 deps.requires[path][require] = true;
        323 }
        324 }
        325};
        326
        327
        328
        329
        330// NOTE(nnaze): The debug DOM loader was included in base.js as an orignal
        331// way to do "debug-mode" development. The dependency system can sometimes
        332// be confusing, as can the debug DOM loader's asyncronous nature.
        333//
        334// With the DOM loader, a call to goog.require() is not blocking -- the
        335// script will not load until some point after the current script. If a
        336// namespace is needed at runtime, it needs to be defined in a previous
        337// script, or loaded via require() with its registered dependencies.
        338// User-defined namespaces may need their own deps file. See http://go/js_deps,
        339// http://go/genjsdeps, or, externally, DepsWriter.
        340// http://code.google.com/closure/library/docs/depswriter.html
        341//
        342// Because of legacy clients, the DOM loader can't be easily removed from
        343// base.js. Work is being done to make it disableable or replaceable for
        344// different environments (DOM-less JavaScript interpreters like Rhino or V8,
        345// for example). See bootstrap/ for more information.
        346
        347
        348/**
        349 * @define {boolean} Whether to enable the debug loader.
        350 *
        351 * If enabled, a call to goog.require() will attempt to load the namespace by
        352 * appending a script tag to the DOM (if the namespace has been registered).
        353 *
        354 * If disabled, goog.require() will simply assert that the namespace has been
        355 * provided (and depend on the fact that some outside tool correctly ordered
        356 * the script).
        357 */
        358goog.define('goog.ENABLE_DEBUG_LOADER', true);
        359
        360
        361/**
        362 * Implements a system for the dynamic resolution of dependencies
        363 * that works in parallel with the BUILD system. Note that all calls
        364 * to goog.require will be stripped by the JSCompiler when the
        365 * --closure_pass option is used.
        366 * @see goog.provide
        367 * @param {string} name Namespace to include (as was given in goog.provide())
        368 * in the form "goog.package.part".
        369 */
        370goog.require = function(name) {
        371
        372 // if the object already exists we do not need do do anything
        373 // TODO(arv): If we start to support require based on file name this has
        374 // to change
        375 // TODO(arv): If we allow goog.foo.* this has to change
        376 // TODO(arv): If we implement dynamic load after page load we should probably
        377 // not remove this code for the compiled output
        378 if (!COMPILED) {
        379 if (goog.isProvided_(name)) {
        380 return;
        381 }
        382
        383 if (goog.ENABLE_DEBUG_LOADER) {
        384 var path = goog.getPathFromDeps_(name);
        385 if (path) {
        386 goog.included_[path] = true;
        387 goog.writeScripts_();
        388 return;
        389 }
        390 }
        391
        392 var errorMessage = 'goog.require could not find: ' + name;
        393 if (goog.global.console) {
        394 goog.global.console['error'](errorMessage);
        395 }
        396
        397
        398 throw Error(errorMessage);
        399
        400 }
        401};
        402
        403
        404/**
        405 * Path for included scripts
        406 * @type {string}
        407 */
        408goog.basePath = '';
        409
        410
        411/**
        412 * A hook for overriding the base path.
        413 * @type {string|undefined}
        414 */
        415goog.global.CLOSURE_BASE_PATH;
        416
        417
        418/**
        419 * Whether to write out Closure's deps file. By default,
        420 * the deps are written.
        421 * @type {boolean|undefined}
        422 */
        423goog.global.CLOSURE_NO_DEPS;
        424
        425
        426/**
        427 * A function to import a single script. This is meant to be overridden when
        428 * Closure is being run in non-HTML contexts, such as web workers. It's defined
        429 * in the global scope so that it can be set before base.js is loaded, which
        430 * allows deps.js to be imported properly.
        431 *
        432 * The function is passed the script source, which is a relative URI. It should
        433 * return true if the script was imported, false otherwise.
        434 */
        435goog.global.CLOSURE_IMPORT_SCRIPT;
        436
        437
        438/**
        439 * Null function used for default values of callbacks, etc.
        440 * @return {void} Nothing.
        441 */
        442goog.nullFunction = function() {};
        443
        444
        445/**
        446 * The identity function. Returns its first argument.
        447 *
        448 * @param {*=} opt_returnValue The single value that will be returned.
        449 * @param {...*} var_args Optional trailing arguments. These are ignored.
        450 * @return {?} The first argument. We can't know the type -- just pass it along
        451 * without type.
        452 * @deprecated Use goog.functions.identity instead.
        453 */
        454goog.identityFunction = function(opt_returnValue, var_args) {
        455 return opt_returnValue;
        456};
        457
        458
        459/**
        460 * When defining a class Foo with an abstract method bar(), you can do:
        461 *
        462 * Foo.prototype.bar = goog.abstractMethod
        463 *
        464 * Now if a subclass of Foo fails to override bar(), an error
        465 * will be thrown when bar() is invoked.
        466 *
        467 * Note: This does not take the name of the function to override as
        468 * an argument because that would make it more difficult to obfuscate
        469 * our JavaScript code.
        470 *
        471 * @type {!Function}
        472 * @throws {Error} when invoked to indicate the method should be
        473 * overridden.
        474 */
        475goog.abstractMethod = function() {
        476 throw Error('unimplemented abstract method');
        477};
        478
        479
        480/**
        481 * Adds a {@code getInstance} static method that always return the same instance
        482 * object.
        483 * @param {!Function} ctor The constructor for the class to add the static
        484 * method to.
        485 */
        486goog.addSingletonGetter = function(ctor) {
        487 ctor.getInstance = function() {
        488 if (ctor.instance_) {
        489 return ctor.instance_;
        490 }
        491 if (goog.DEBUG) {
        492 // NOTE: JSCompiler can't optimize away Array#push.
        493 goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
        494 }
        495 return ctor.instance_ = new ctor;
        496 };
        497};
        498
        499
        500/**
        501 * All singleton classes that have been instantiated, for testing. Don't read
        502 * it directly, use the {@code goog.testing.singleton} module. The compiler
        503 * removes this variable if unused.
        504 * @type {!Array.<!Function>}
        505 * @private
        506 */
        507goog.instantiatedSingletons_ = [];
        508
        509
        510/**
        511 * True if goog.dependencies_ is available.
        512 * @const {boolean}
        513 */
        514goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
        515
        516
        517if (goog.DEPENDENCIES_ENABLED) {
        518 /**
        519 * Object used to keep track of urls that have already been added. This
        520 * record allows the prevention of circular dependencies.
        521 * @type {Object}
        522 * @private
        523 */
        524 goog.included_ = {};
        525
        526
        527 /**
        528 * This object is used to keep track of dependencies and other data that is
        529 * used for loading scripts
        530 * @private
        531 * @type {Object}
        532 */
        533 goog.dependencies_ = {
        534 pathToNames: {}, // 1 to many
        535 nameToPath: {}, // 1 to 1
        536 requires: {}, // 1 to many
        537 // used when resolving dependencies to prevent us from
        538 // visiting the file twice
        539 visited: {},
        540 written: {} // used to keep track of script files we have written
        541 };
        542
        543
        544 /**
        545 * Tries to detect whether is in the context of an HTML document.
        546 * @return {boolean} True if it looks like HTML document.
        547 * @private
        548 */
        549 goog.inHtmlDocument_ = function() {
        550 var doc = goog.global.document;
        551 return typeof doc != 'undefined' &&
        552 'write' in doc; // XULDocument misses write.
        553 };
        554
        555
        556 /**
        557 * Tries to detect the base path of the base.js script that bootstraps Closure
        558 * @private
        559 */
        560 goog.findBasePath_ = function() {
        561 if (goog.global.CLOSURE_BASE_PATH) {
        562 goog.basePath = goog.global.CLOSURE_BASE_PATH;
        563 return;
        564 } else if (!goog.inHtmlDocument_()) {
        565 return;
        566 }
        567 var doc = goog.global.document;
        568 var scripts = doc.getElementsByTagName('script');
        569 // Search backwards since the current script is in almost all cases the one
        570 // that has base.js.
        571 for (var i = scripts.length - 1; i >= 0; --i) {
        572 var src = scripts[i].src;
        573 var qmark = src.lastIndexOf('?');
        574 var l = qmark == -1 ? src.length : qmark;
        575 if (src.substr(l - 7, 7) == 'base.js') {
        576 goog.basePath = src.substr(0, l - 7);
        577 return;
        578 }
        579 }
        580 };
        581
        582
        583 /**
        584 * Imports a script if, and only if, that script hasn't already been imported.
        585 * (Must be called at execution time)
        586 * @param {string} src Script source.
        587 * @private
        588 */
        589 goog.importScript_ = function(src) {
        590 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
        591 goog.writeScriptTag_;
        592 if (!goog.dependencies_.written[src] && importScript(src)) {
        593 goog.dependencies_.written[src] = true;
        594 }
        595 };
        596
        597
        598 /**
        599 * The default implementation of the import function. Writes a script tag to
        600 * import the script.
        601 *
        602 * @param {string} src The script source.
        603 * @return {boolean} True if the script was imported, false otherwise.
        604 * @private
        605 */
        606 goog.writeScriptTag_ = function(src) {
        607 if (goog.inHtmlDocument_()) {
        608 var doc = goog.global.document;
        609
        610 // If the user tries to require a new symbol after document load,
        611 // something has gone terribly wrong. Doing a document.write would
        612 // wipe out the page.
        613 if (doc.readyState == 'complete') {
        614 // Certain test frameworks load base.js multiple times, which tries
        615 // to write deps.js each time. If that happens, just fail silently.
        616 // These frameworks wipe the page between each load of base.js, so this
        617 // is OK.
        618 var isDeps = /\bdeps.js$/.test(src);
        619 if (isDeps) {
        620 return false;
        621 } else {
        622 throw Error('Cannot write "' + src + '" after document load');
        623 }
        624 }
        625
        626 doc.write(
        627 '<script type="text/javascript" src="' + src + '"></' + 'script>');
        628 return true;
        629 } else {
        630 return false;
        631 }
        632 };
        633
        634
        635 /**
        636 * Resolves dependencies based on the dependencies added using addDependency
        637 * and calls importScript_ in the correct order.
        638 * @private
        639 */
        640 goog.writeScripts_ = function() {
        641 // the scripts we need to write this time
        642 var scripts = [];
        643 var seenScript = {};
        644 var deps = goog.dependencies_;
        645
        646 function visitNode(path) {
        647 if (path in deps.written) {
        648 return;
        649 }
        650
        651 // we have already visited this one. We can get here if we have cyclic
        652 // dependencies
        653 if (path in deps.visited) {
        654 if (!(path in seenScript)) {
        655 seenScript[path] = true;
        656 scripts.push(path);
        657 }
        658 return;
        659 }
        660
        661 deps.visited[path] = true;
        662
        663 if (path in deps.requires) {
        664 for (var requireName in deps.requires[path]) {
        665 // If the required name is defined, we assume that it was already
        666 // bootstrapped by other means.
        667 if (!goog.isProvided_(requireName)) {
        668 if (requireName in deps.nameToPath) {
        669 visitNode(deps.nameToPath[requireName]);
        670 } else {
        671 throw Error('Undefined nameToPath for ' + requireName);
        672 }
        673 }
        674 }
        675 }
        676
        677 if (!(path in seenScript)) {
        678 seenScript[path] = true;
        679 scripts.push(path);
        680 }
        681 }
        682
        683 for (var path in goog.included_) {
        684 if (!deps.written[path]) {
        685 visitNode(path);
        686 }
        687 }
        688
        689 for (var i = 0; i < scripts.length; i++) {
        690 if (scripts[i]) {
        691 goog.importScript_(goog.basePath + scripts[i]);
        692 } else {
        693 throw Error('Undefined script input');
        694 }
        695 }
        696 };
        697
        698
        699 /**
        700 * Looks at the dependency rules and tries to determine the script file that
        701 * fulfills a particular rule.
        702 * @param {string} rule In the form goog.namespace.Class or project.script.
        703 * @return {?string} Url corresponding to the rule, or null.
        704 * @private
        705 */
        706 goog.getPathFromDeps_ = function(rule) {
        707 if (rule in goog.dependencies_.nameToPath) {
        708 return goog.dependencies_.nameToPath[rule];
        709 } else {
        710 return null;
        711 }
        712 };
        713
        714 goog.findBasePath_();
        715
        716 // Allow projects to manage the deps files themselves.
        717 if (!goog.global.CLOSURE_NO_DEPS) {
        718 goog.importScript_(goog.basePath + 'deps.js');
        719 }
        720}
        721
        722
        723
        724//==============================================================================
        725// Language Enhancements
        726//==============================================================================
        727
        728
        729/**
        730 * This is a "fixed" version of the typeof operator. It differs from the typeof
        731 * operator in such a way that null returns 'null' and arrays return 'array'.
        732 * @param {*} value The value to get the type of.
        733 * @return {string} The name of the type.
        734 */
        735goog.typeOf = function(value) {
        736 var s = typeof value;
        737 if (s == 'object') {
        738 if (value) {
        739 // Check these first, so we can avoid calling Object.prototype.toString if
        740 // possible.
        741 //
        742 // IE improperly marshals tyepof across execution contexts, but a
        743 // cross-context object will still return false for "instanceof Object".
        744 if (value instanceof Array) {
        745 return 'array';
        746 } else if (value instanceof Object) {
        747 return s;
        748 }
        749
        750 // HACK: In order to use an Object prototype method on the arbitrary
        751 // value, the compiler requires the value be cast to type Object,
        752 // even though the ECMA spec explicitly allows it.
        753 var className = Object.prototype.toString.call(
        754 /** @type {Object} */ (value));
        755 // In Firefox 3.6, attempting to access iframe window objects' length
        756 // property throws an NS_ERROR_FAILURE, so we need to special-case it
        757 // here.
        758 if (className == '[object Window]') {
        759 return 'object';
        760 }
        761
        762 // We cannot always use constructor == Array or instanceof Array because
        763 // different frames have different Array objects. In IE6, if the iframe
        764 // where the array was created is destroyed, the array loses its
        765 // prototype. Then dereferencing val.splice here throws an exception, so
        766 // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
        767 // so that will work. In this case, this function will return false and
        768 // most array functions will still work because the array is still
        769 // array-like (supports length and []) even though it has lost its
        770 // prototype.
        771 // Mark Miller noticed that Object.prototype.toString
        772 // allows access to the unforgeable [[Class]] property.
        773 // 15.2.4.2 Object.prototype.toString ( )
        774 // When the toString method is called, the following steps are taken:
        775 // 1. Get the [[Class]] property of this object.
        776 // 2. Compute a string value by concatenating the three strings
        777 // "[object ", Result(1), and "]".
        778 // 3. Return Result(2).
        779 // and this behavior survives the destruction of the execution context.
        780 if ((className == '[object Array]' ||
        781 // In IE all non value types are wrapped as objects across window
        782 // boundaries (not iframe though) so we have to do object detection
        783 // for this edge case
        784 typeof value.length == 'number' &&
        785 typeof value.splice != 'undefined' &&
        786 typeof value.propertyIsEnumerable != 'undefined' &&
        787 !value.propertyIsEnumerable('splice')
        788
        789 )) {
        790 return 'array';
        791 }
        792 // HACK: There is still an array case that fails.
        793 // function ArrayImpostor() {}
        794 // ArrayImpostor.prototype = [];
        795 // var impostor = new ArrayImpostor;
        796 // this can be fixed by getting rid of the fast path
        797 // (value instanceof Array) and solely relying on
        798 // (value && Object.prototype.toString.vall(value) === '[object Array]')
        799 // but that would require many more function calls and is not warranted
        800 // unless closure code is receiving objects from untrusted sources.
        801
        802 // IE in cross-window calls does not correctly marshal the function type
        803 // (it appears just as an object) so we cannot use just typeof val ==
        804 // 'function'. However, if the object has a call property, it is a
        805 // function.
        806 if ((className == '[object Function]' ||
        807 typeof value.call != 'undefined' &&
        808 typeof value.propertyIsEnumerable != 'undefined' &&
        809 !value.propertyIsEnumerable('call'))) {
        810 return 'function';
        811 }
        812
        813
        814 } else {
        815 return 'null';
        816 }
        817
        818 } else if (s == 'function' && typeof value.call == 'undefined') {
        819 // In Safari typeof nodeList returns 'function', and on Firefox
        820 // typeof behaves similarly for HTML{Applet,Embed,Object}Elements
        821 // and RegExps. We would like to return object for those and we can
        822 // detect an invalid function by making sure that the function
        823 // object has a call method.
        824 return 'object';
        825 }
        826 return s;
        827};
        828
        829
        830/**
        831 * Returns true if the specified value is not |undefined|.
        832 * WARNING: Do not use this to test if an object has a property. Use the in
        833 * operator instead. Additionally, this function assumes that the global
        834 * undefined variable has not been redefined.
        835 * @param {*} val Variable to test.
        836 * @return {boolean} Whether variable is defined.
        837 */
        838goog.isDef = function(val) {
        839 return val !== undefined;
        840};
        841
        842
        843/**
        844 * Returns true if the specified value is |null|
        845 * @param {*} val Variable to test.
        846 * @return {boolean} Whether variable is null.
        847 */
        848goog.isNull = function(val) {
        849 return val === null;
        850};
        851
        852
        853/**
        854 * Returns true if the specified value is defined and not null
        855 * @param {*} val Variable to test.
        856 * @return {boolean} Whether variable is defined and not null.
        857 */
        858goog.isDefAndNotNull = function(val) {
        859 // Note that undefined == null.
        860 return val != null;
        861};
        862
        863
        864/**
        865 * Returns true if the specified value is an array
        866 * @param {*} val Variable to test.
        867 * @return {boolean} Whether variable is an array.
        868 */
        869goog.isArray = function(val) {
        870 return goog.typeOf(val) == 'array';
        871};
        872
        873
        874/**
        875 * Returns true if the object looks like an array. To qualify as array like
        876 * the value needs to be either a NodeList or an object with a Number length
        877 * property.
        878 * @param {*} val Variable to test.
        879 * @return {boolean} Whether variable is an array.
        880 */
        881goog.isArrayLike = function(val) {
        882 var type = goog.typeOf(val);
        883 return type == 'array' || type == 'object' && typeof val.length == 'number';
        884};
        885
        886
        887/**
        888 * Returns true if the object looks like a Date. To qualify as Date-like
        889 * the value needs to be an object and have a getFullYear() function.
        890 * @param {*} val Variable to test.
        891 * @return {boolean} Whether variable is a like a Date.
        892 */
        893goog.isDateLike = function(val) {
        894 return goog.isObject(val) && typeof val.getFullYear == 'function';
        895};
        896
        897
        898/**
        899 * Returns true if the specified value is a string
        900 * @param {*} val Variable to test.
        901 * @return {boolean} Whether variable is a string.
        902 */
        903goog.isString = function(val) {
        904 return typeof val == 'string';
        905};
        906
        907
        908/**
        909 * Returns true if the specified value is a boolean
        910 * @param {*} val Variable to test.
        911 * @return {boolean} Whether variable is boolean.
        912 */
        913goog.isBoolean = function(val) {
        914 return typeof val == 'boolean';
        915};
        916
        917
        918/**
        919 * Returns true if the specified value is a number
        920 * @param {*} val Variable to test.
        921 * @return {boolean} Whether variable is a number.
        922 */
        923goog.isNumber = function(val) {
        924 return typeof val == 'number';
        925};
        926
        927
        928/**
        929 * Returns true if the specified value is a function
        930 * @param {*} val Variable to test.
        931 * @return {boolean} Whether variable is a function.
        932 */
        933goog.isFunction = function(val) {
        934 return goog.typeOf(val) == 'function';
        935};
        936
        937
        938/**
        939 * Returns true if the specified value is an object. This includes arrays
        940 * and functions.
        941 * @param {*} val Variable to test.
        942 * @return {boolean} Whether variable is an object.
        943 */
        944goog.isObject = function(val) {
        945 var type = typeof val;
        946 return type == 'object' && val != null || type == 'function';
        947 // return Object(val) === val also works, but is slower, especially if val is
        948 // not an object.
        949};
        950
        951
        952/**
        953 * Gets a unique ID for an object. This mutates the object so that further
        954 * calls with the same object as a parameter returns the same value. The unique
        955 * ID is guaranteed to be unique across the current session amongst objects that
        956 * are passed into {@code getUid}. There is no guarantee that the ID is unique
        957 * or consistent across sessions. It is unsafe to generate unique ID for
        958 * function prototypes.
        959 *
        960 * @param {Object} obj The object to get the unique ID for.
        961 * @return {number} The unique ID for the object.
        962 */
        963goog.getUid = function(obj) {
        964 // TODO(arv): Make the type stricter, do not accept null.
        965
        966 // In Opera window.hasOwnProperty exists but always returns false so we avoid
        967 // using it. As a consequence the unique ID generated for BaseClass.prototype
        968 // and SubClass.prototype will be the same.
        969 return obj[goog.UID_PROPERTY_] ||
        970 (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
        971};
        972
        973
        974/**
        975 * Removes the unique ID from an object. This is useful if the object was
        976 * previously mutated using {@code goog.getUid} in which case the mutation is
        977 * undone.
        978 * @param {Object} obj The object to remove the unique ID field from.
        979 */
        980goog.removeUid = function(obj) {
        981 // TODO(arv): Make the type stricter, do not accept null.
        982
        983 // DOM nodes in IE are not instance of Object and throws exception
        984 // for delete. Instead we try to use removeAttribute
        985 if ('removeAttribute' in obj) {
        986 obj.removeAttribute(goog.UID_PROPERTY_);
        987 }
        988 /** @preserveTry */
        989 try {
        990 delete obj[goog.UID_PROPERTY_];
        991 } catch (ex) {
        992 }
        993};
        994
        995
        996/**
        997 * Name for unique ID property. Initialized in a way to help avoid collisions
        998 * with other closure javascript on the same page.
        999 * @type {string}
        1000 * @private
        1001 */
        1002goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);
        1003
        1004
        1005/**
        1006 * Counter for UID.
        1007 * @type {number}
        1008 * @private
        1009 */
        1010goog.uidCounter_ = 0;
        1011
        1012
        1013/**
        1014 * Adds a hash code field to an object. The hash code is unique for the
        1015 * given object.
        1016 * @param {Object} obj The object to get the hash code for.
        1017 * @return {number} The hash code for the object.
        1018 * @deprecated Use goog.getUid instead.
        1019 */
        1020goog.getHashCode = goog.getUid;
        1021
        1022
        1023/**
        1024 * Removes the hash code field from an object.
        1025 * @param {Object} obj The object to remove the field from.
        1026 * @deprecated Use goog.removeUid instead.
        1027 */
        1028goog.removeHashCode = goog.removeUid;
        1029
        1030
        1031/**
        1032 * Clones a value. The input may be an Object, Array, or basic type. Objects and
        1033 * arrays will be cloned recursively.
        1034 *
        1035 * WARNINGS:
        1036 * <code>goog.cloneObject</code> does not detect reference loops. Objects that
        1037 * refer to themselves will cause infinite recursion.
        1038 *
        1039 * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
        1040 * UIDs created by <code>getUid</code> into cloned results.
        1041 *
        1042 * @param {*} obj The value to clone.
        1043 * @return {*} A clone of the input value.
        1044 * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
        1045 */
        1046goog.cloneObject = function(obj) {
        1047 var type = goog.typeOf(obj);
        1048 if (type == 'object' || type == 'array') {
        1049 if (obj.clone) {
        1050 return obj.clone();
        1051 }
        1052 var clone = type == 'array' ? [] : {};
        1053 for (var key in obj) {
        1054 clone[key] = goog.cloneObject(obj[key]);
        1055 }
        1056 return clone;
        1057 }
        1058
        1059 return obj;
        1060};
        1061
        1062
        1063/**
        1064 * A native implementation of goog.bind.
        1065 * @param {Function} fn A function to partially apply.
        1066 * @param {Object|undefined} selfObj Specifies the object which |this| should
        1067 * point to when the function is run.
        1068 * @param {...*} var_args Additional arguments that are partially
        1069 * applied to the function.
        1070 * @return {!Function} A partially-applied form of the function bind() was
        1071 * invoked as a method of.
        1072 * @private
        1073 * @suppress {deprecated} The compiler thinks that Function.prototype.bind
        1074 * is deprecated because some people have declared a pure-JS version.
        1075 * Only the pure-JS version is truly deprecated.
        1076 */
        1077goog.bindNative_ = function(fn, selfObj, var_args) {
        1078 return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
        1079};
        1080
        1081
        1082/**
        1083 * A pure-JS implementation of goog.bind.
        1084 * @param {Function} fn A function to partially apply.
        1085 * @param {Object|undefined} selfObj Specifies the object which |this| should
        1086 * point to when the function is run.
        1087 * @param {...*} var_args Additional arguments that are partially
        1088 * applied to the function.
        1089 * @return {!Function} A partially-applied form of the function bind() was
        1090 * invoked as a method of.
        1091 * @private
        1092 */
        1093goog.bindJs_ = function(fn, selfObj, var_args) {
        1094 if (!fn) {
        1095 throw new Error();
        1096 }
        1097
        1098 if (arguments.length > 2) {
        1099 var boundArgs = Array.prototype.slice.call(arguments, 2);
        1100 return function() {
        1101 // Prepend the bound arguments to the current arguments.
        1102 var newArgs = Array.prototype.slice.call(arguments);
        1103 Array.prototype.unshift.apply(newArgs, boundArgs);
        1104 return fn.apply(selfObj, newArgs);
        1105 };
        1106
        1107 } else {
        1108 return function() {
        1109 return fn.apply(selfObj, arguments);
        1110 };
        1111 }
        1112};
        1113
        1114
        1115/**
        1116 * Partially applies this function to a particular 'this object' and zero or
        1117 * more arguments. The result is a new function with some arguments of the first
        1118 * function pre-filled and the value of |this| 'pre-specified'.<br><br>
        1119 *
        1120 * Remaining arguments specified at call-time are appended to the pre-
        1121 * specified ones.<br><br>
        1122 *
        1123 * Also see: {@link #partial}.<br><br>
        1124 *
        1125 * Usage:
        1126 * <pre>var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2');
        1127 * barMethBound('arg3', 'arg4');</pre>
        1128 *
        1129 * @param {?function(this:T, ...)} fn A function to partially apply.
        1130 * @param {T} selfObj Specifies the object which |this| should
        1131 * point to when the function is run.
        1132 * @param {...*} var_args Additional arguments that are partially
        1133 * applied to the function.
        1134 * @return {!Function} A partially-applied form of the function bind() was
        1135 * invoked as a method of.
        1136 * @template T
        1137 * @suppress {deprecated} See above.
        1138 */
        1139goog.bind = function(fn, selfObj, var_args) {
        1140 // TODO(nicksantos): narrow the type signature.
        1141 if (Function.prototype.bind &&
        1142 // NOTE(nicksantos): Somebody pulled base.js into the default
        1143 // Chrome extension environment. This means that for Chrome extensions,
        1144 // they get the implementation of Function.prototype.bind that
        1145 // calls goog.bind instead of the native one. Even worse, we don't want
        1146 // to introduce a circular dependency between goog.bind and
        1147 // Function.prototype.bind, so we have to hack this to make sure it
        1148 // works correctly.
        1149 Function.prototype.bind.toString().indexOf('native code') != -1) {
        1150 goog.bind = goog.bindNative_;
        1151 } else {
        1152 goog.bind = goog.bindJs_;
        1153 }
        1154 return goog.bind.apply(null, arguments);
        1155};
        1156
        1157
        1158/**
        1159 * Like bind(), except that a 'this object' is not required. Useful when the
        1160 * target function is already bound.
        1161 *
        1162 * Usage:
        1163 * var g = partial(f, arg1, arg2);
        1164 * g(arg3, arg4);
        1165 *
        1166 * @param {Function} fn A function to partially apply.
        1167 * @param {...*} var_args Additional arguments that are partially
        1168 * applied to fn.
        1169 * @return {!Function} A partially-applied form of the function bind() was
        1170 * invoked as a method of.
        1171 */
        1172goog.partial = function(fn, var_args) {
        1173 var args = Array.prototype.slice.call(arguments, 1);
        1174 return function() {
        1175 // Prepend the bound arguments to the current arguments.
        1176 var newArgs = Array.prototype.slice.call(arguments);
        1177 newArgs.unshift.apply(newArgs, args);
        1178 return fn.apply(this, newArgs);
        1179 };
        1180};
        1181
        1182
        1183/**
        1184 * Copies all the members of a source object to a target object. This method
        1185 * does not work on all browsers for all objects that contain keys such as
        1186 * toString or hasOwnProperty. Use goog.object.extend for this purpose.
        1187 * @param {Object} target Target.
        1188 * @param {Object} source Source.
        1189 */
        1190goog.mixin = function(target, source) {
        1191 for (var x in source) {
        1192 target[x] = source[x];
        1193 }
        1194
        1195 // For IE7 or lower, the for-in-loop does not contain any properties that are
        1196 // not enumerable on the prototype object (for example, isPrototypeOf from
        1197 // Object.prototype) but also it will not include 'replace' on objects that
        1198 // extend String and change 'replace' (not that it is common for anyone to
        1199 // extend anything except Object).
        1200};
        1201
        1202
        1203/**
        1204 * @return {number} An integer value representing the number of milliseconds
        1205 * between midnight, January 1, 1970 and the current time.
        1206 */
        1207goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
        1208 // Unary plus operator converts its operand to a number which in the case of
        1209 // a date is done by calling getTime().
        1210 return +new Date();
        1211});
        1212
        1213
        1214/**
        1215 * Evals javascript in the global scope. In IE this uses execScript, other
        1216 * browsers use goog.global.eval. If goog.global.eval does not evaluate in the
        1217 * global scope (for example, in Safari), appends a script tag instead.
        1218 * Throws an exception if neither execScript or eval is defined.
        1219 * @param {string} script JavaScript string.
        1220 */
        1221goog.globalEval = function(script) {
        1222 if (goog.global.execScript) {
        1223 goog.global.execScript(script, 'JavaScript');
        1224 } else if (goog.global.eval) {
        1225 // Test to see if eval works
        1226 if (goog.evalWorksForGlobals_ == null) {
        1227 goog.global.eval('var _et_ = 1;');
        1228 if (typeof goog.global['_et_'] != 'undefined') {
        1229 delete goog.global['_et_'];
        1230 goog.evalWorksForGlobals_ = true;
        1231 } else {
        1232 goog.evalWorksForGlobals_ = false;
        1233 }
        1234 }
        1235
        1236 if (goog.evalWorksForGlobals_) {
        1237 goog.global.eval(script);
        1238 } else {
        1239 var doc = goog.global.document;
        1240 var scriptElt = doc.createElement('script');
        1241 scriptElt.type = 'text/javascript';
        1242 scriptElt.defer = false;
        1243 // Note(user): can't use .innerHTML since "t('<test>')" will fail and
        1244 // .text doesn't work in Safari 2. Therefore we append a text node.
        1245 scriptElt.appendChild(doc.createTextNode(script));
        1246 doc.body.appendChild(scriptElt);
        1247 doc.body.removeChild(scriptElt);
        1248 }
        1249 } else {
        1250 throw Error('goog.globalEval not available');
        1251 }
        1252};
        1253
        1254
        1255/**
        1256 * Indicates whether or not we can call 'eval' directly to eval code in the
        1257 * global scope. Set to a Boolean by the first call to goog.globalEval (which
        1258 * empirically tests whether eval works for globals). @see goog.globalEval
        1259 * @type {?boolean}
        1260 * @private
        1261 */
        1262goog.evalWorksForGlobals_ = null;
        1263
        1264
        1265/**
        1266 * Optional map of CSS class names to obfuscated names used with
        1267 * goog.getCssName().
        1268 * @type {Object|undefined}
        1269 * @private
        1270 * @see goog.setCssNameMapping
        1271 */
        1272goog.cssNameMapping_;
        1273
        1274
        1275/**
        1276 * Optional obfuscation style for CSS class names. Should be set to either
        1277 * 'BY_WHOLE' or 'BY_PART' if defined.
        1278 * @type {string|undefined}
        1279 * @private
        1280 * @see goog.setCssNameMapping
        1281 */
        1282goog.cssNameMappingStyle_;
        1283
        1284
        1285/**
        1286 * Handles strings that are intended to be used as CSS class names.
        1287 *
        1288 * This function works in tandem with @see goog.setCssNameMapping.
        1289 *
        1290 * Without any mapping set, the arguments are simple joined with a
        1291 * hyphen and passed through unaltered.
        1292 *
        1293 * When there is a mapping, there are two possible styles in which
        1294 * these mappings are used. In the BY_PART style, each part (i.e. in
        1295 * between hyphens) of the passed in css name is rewritten according
        1296 * to the map. In the BY_WHOLE style, the full css name is looked up in
        1297 * the map directly. If a rewrite is not specified by the map, the
        1298 * compiler will output a warning.
        1299 *
        1300 * When the mapping is passed to the compiler, it will replace calls
        1301 * to goog.getCssName with the strings from the mapping, e.g.
        1302 * var x = goog.getCssName('foo');
        1303 * var y = goog.getCssName(this.baseClass, 'active');
        1304 * becomes:
        1305 * var x= 'foo';
        1306 * var y = this.baseClass + '-active';
        1307 *
        1308 * If one argument is passed it will be processed, if two are passed
        1309 * only the modifier will be processed, as it is assumed the first
        1310 * argument was generated as a result of calling goog.getCssName.
        1311 *
        1312 * @param {string} className The class name.
        1313 * @param {string=} opt_modifier A modifier to be appended to the class name.
        1314 * @return {string} The class name or the concatenation of the class name and
        1315 * the modifier.
        1316 */
        1317goog.getCssName = function(className, opt_modifier) {
        1318 var getMapping = function(cssName) {
        1319 return goog.cssNameMapping_[cssName] || cssName;
        1320 };
        1321
        1322 var renameByParts = function(cssName) {
        1323 // Remap all the parts individually.
        1324 var parts = cssName.split('-');
        1325 var mapped = [];
        1326 for (var i = 0; i < parts.length; i++) {
        1327 mapped.push(getMapping(parts[i]));
        1328 }
        1329 return mapped.join('-');
        1330 };
        1331
        1332 var rename;
        1333 if (goog.cssNameMapping_) {
        1334 rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ?
        1335 getMapping : renameByParts;
        1336 } else {
        1337 rename = function(a) {
        1338 return a;
        1339 };
        1340 }
        1341
        1342 if (opt_modifier) {
        1343 return className + '-' + rename(opt_modifier);
        1344 } else {
        1345 return rename(className);
        1346 }
        1347};
        1348
        1349
        1350/**
        1351 * Sets the map to check when returning a value from goog.getCssName(). Example:
        1352 * <pre>
        1353 * goog.setCssNameMapping({
        1354 * "goog": "a",
        1355 * "disabled": "b",
        1356 * });
        1357 *
        1358 * var x = goog.getCssName('goog');
        1359 * // The following evaluates to: "a a-b".
        1360 * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
        1361 * </pre>
        1362 * When declared as a map of string literals to string literals, the JSCompiler
        1363 * will replace all calls to goog.getCssName() using the supplied map if the
        1364 * --closure_pass flag is set.
        1365 *
        1366 * @param {!Object} mapping A map of strings to strings where keys are possible
        1367 * arguments to goog.getCssName() and values are the corresponding values
        1368 * that should be returned.
        1369 * @param {string=} opt_style The style of css name mapping. There are two valid
        1370 * options: 'BY_PART', and 'BY_WHOLE'.
        1371 * @see goog.getCssName for a description.
        1372 */
        1373goog.setCssNameMapping = function(mapping, opt_style) {
        1374 goog.cssNameMapping_ = mapping;
        1375 goog.cssNameMappingStyle_ = opt_style;
        1376};
        1377
        1378
        1379/**
        1380 * To use CSS renaming in compiled mode, one of the input files should have a
        1381 * call to goog.setCssNameMapping() with an object literal that the JSCompiler
        1382 * can extract and use to replace all calls to goog.getCssName(). In uncompiled
        1383 * mode, JavaScript code should be loaded before this base.js file that declares
        1384 * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
        1385 * to ensure that the mapping is loaded before any calls to goog.getCssName()
        1386 * are made in uncompiled mode.
        1387 *
        1388 * A hook for overriding the CSS name mapping.
        1389 * @type {Object|undefined}
        1390 */
        1391goog.global.CLOSURE_CSS_NAME_MAPPING;
        1392
        1393
        1394if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
        1395 // This does not call goog.setCssNameMapping() because the JSCompiler
        1396 // requires that goog.setCssNameMapping() be called with an object literal.
        1397 goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
        1398}
        1399
        1400
        1401/**
        1402 * Gets a localized message.
        1403 *
        1404 * This function is a compiler primitive. If you give the compiler a localized
        1405 * message bundle, it will replace the string at compile-time with a localized
        1406 * version, and expand goog.getMsg call to a concatenated string.
        1407 *
        1408 * Messages must be initialized in the form:
        1409 * <code>
        1410 * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
        1411 * </code>
        1412 *
        1413 * @param {string} str Translatable string, places holders in the form {$foo}.
        1414 * @param {Object=} opt_values Map of place holder name to value.
        1415 * @return {string} message with placeholders filled.
        1416 */
        1417goog.getMsg = function(str, opt_values) {
        1418 var values = opt_values || {};
        1419 for (var key in values) {
        1420 var value = ('' + values[key]).replace(/\$/g, '$$$$');
        1421 str = str.replace(new RegExp('\\{\\$' + key + '\\}', 'gi'), value);
        1422 }
        1423 return str;
        1424};
        1425
        1426
        1427/**
        1428 * Gets a localized message. If the message does not have a translation, gives a
        1429 * fallback message.
        1430 *
        1431 * This is useful when introducing a new message that has not yet been
        1432 * translated into all languages.
        1433 *
        1434 * This function is a compiler primtive. Must be used in the form:
        1435 * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
        1436 * where MSG_A and MSG_B were initialized with goog.getMsg.
        1437 *
        1438 * @param {string} a The preferred message.
        1439 * @param {string} b The fallback message.
        1440 * @return {string} The best translated message.
        1441 */
        1442goog.getMsgWithFallback = function(a, b) {
        1443 return a;
        1444};
        1445
        1446
        1447/**
        1448 * Exposes an unobfuscated global namespace path for the given object.
        1449 * Note that fields of the exported object *will* be obfuscated,
        1450 * unless they are exported in turn via this function or
        1451 * goog.exportProperty
        1452 *
        1453 * <p>Also handy for making public items that are defined in anonymous
        1454 * closures.
        1455 *
        1456 * ex. goog.exportSymbol('public.path.Foo', Foo);
        1457 *
        1458 * ex. goog.exportSymbol('public.path.Foo.staticFunction',
        1459 * Foo.staticFunction);
        1460 * public.path.Foo.staticFunction();
        1461 *
        1462 * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
        1463 * Foo.prototype.myMethod);
        1464 * new public.path.Foo().myMethod();
        1465 *
        1466 * @param {string} publicPath Unobfuscated name to export.
        1467 * @param {*} object Object the name should point to.
        1468 * @param {Object=} opt_objectToExportTo The object to add the path to; default
        1469 * is |goog.global|.
        1470 */
        1471goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
        1472 goog.exportPath_(publicPath, object, opt_objectToExportTo);
        1473};
        1474
        1475
        1476/**
        1477 * Exports a property unobfuscated into the object's namespace.
        1478 * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
        1479 * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
        1480 * @param {Object} object Object whose static property is being exported.
        1481 * @param {string} publicName Unobfuscated name to export.
        1482 * @param {*} symbol Object the name should point to.
        1483 */
        1484goog.exportProperty = function(object, publicName, symbol) {
        1485 object[publicName] = symbol;
        1486};
        1487
        1488
        1489/**
        1490 * Inherit the prototype methods from one constructor into another.
        1491 *
        1492 * Usage:
        1493 * <pre>
        1494 * function ParentClass(a, b) { }
        1495 * ParentClass.prototype.foo = function(a) { }
        1496 *
        1497 * function ChildClass(a, b, c) {
        1498 * goog.base(this, a, b);
        1499 * }
        1500 * goog.inherits(ChildClass, ParentClass);
        1501 *
        1502 * var child = new ChildClass('a', 'b', 'see');
        1503 * child.foo(); // works
        1504 * </pre>
        1505 *
        1506 * In addition, a superclass' implementation of a method can be invoked
        1507 * as follows:
        1508 *
        1509 * <pre>
        1510 * ChildClass.prototype.foo = function(a) {
        1511 * ChildClass.superClass_.foo.call(this, a);
        1512 * // other code
        1513 * };
        1514 * </pre>
        1515 *
        1516 * @param {Function} childCtor Child class.
        1517 * @param {Function} parentCtor Parent class.
        1518 */
        1519goog.inherits = function(childCtor, parentCtor) {
        1520 /** @constructor */
        1521 function tempCtor() {};
        1522 tempCtor.prototype = parentCtor.prototype;
        1523 childCtor.superClass_ = parentCtor.prototype;
        1524 childCtor.prototype = new tempCtor();
        1525 /** @override */
        1526 childCtor.prototype.constructor = childCtor;
        1527};
        1528
        1529
        1530/**
        1531 * Call up to the superclass.
        1532 *
        1533 * If this is called from a constructor, then this calls the superclass
        1534 * contructor with arguments 1-N.
        1535 *
        1536 * If this is called from a prototype method, then you must pass
        1537 * the name of the method as the second argument to this function. If
        1538 * you do not, you will get a runtime error. This calls the superclass'
        1539 * method with arguments 2-N.
        1540 *
        1541 * This function only works if you use goog.inherits to express
        1542 * inheritance relationships between your classes.
        1543 *
        1544 * This function is a compiler primitive. At compile-time, the
        1545 * compiler will do macro expansion to remove a lot of
        1546 * the extra overhead that this function introduces. The compiler
        1547 * will also enforce a lot of the assumptions that this function
        1548 * makes, and treat it as a compiler error if you break them.
        1549 *
        1550 * @param {!Object} me Should always be "this".
        1551 * @param {*=} opt_methodName The method name if calling a super method.
        1552 * @param {...*} var_args The rest of the arguments.
        1553 * @return {*} The return value of the superclass method.
        1554 */
        1555goog.base = function(me, opt_methodName, var_args) {
        1556 var caller = arguments.callee.caller;
        1557
        1558 if (goog.DEBUG) {
        1559 if (!caller) {
        1560 throw Error('arguments.caller not defined. goog.base() expects not ' +
        1561 'to be running in strict mode. See ' +
        1562 'http://www.ecma-international.org/ecma-262/5.1/#sec-C');
        1563 }
        1564 }
        1565
        1566 if (caller.superClass_) {
        1567 // This is a constructor. Call the superclass constructor.
        1568 return caller.superClass_.constructor.apply(
        1569 me, Array.prototype.slice.call(arguments, 1));
        1570 }
        1571
        1572 var args = Array.prototype.slice.call(arguments, 2);
        1573 var foundCaller = false;
        1574 for (var ctor = me.constructor;
        1575 ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
        1576 if (ctor.prototype[opt_methodName] === caller) {
        1577 foundCaller = true;
        1578 } else if (foundCaller) {
        1579 return ctor.prototype[opt_methodName].apply(me, args);
        1580 }
        1581 }
        1582
        1583 // If we did not find the caller in the prototype chain,
        1584 // then one of two things happened:
        1585 // 1) The caller is an instance method.
        1586 // 2) This method was not called by the right caller.
        1587 if (me[opt_methodName] === caller) {
        1588 return me.constructor.prototype[opt_methodName].apply(me, args);
        1589 } else {
        1590 throw Error(
        1591 'goog.base called from a method of one name ' +
        1592 'to a method of a different name');
        1593 }
        1594};
        1595
        1596
        1597/**
        1598 * Allow for aliasing within scope functions. This function exists for
        1599 * uncompiled code - in compiled code the calls will be inlined and the
        1600 * aliases applied. In uncompiled code the function is simply run since the
        1601 * aliases as written are valid JavaScript.
        1602 * @param {function()} fn Function to call. This function can contain aliases
        1603 * to namespaces (e.g. "var dom = goog.dom") or classes
        1604 * (e.g. "var Timer = goog.Timer").
        1605 */
        1606goog.scope = function(fn) {
        1607 fn.call(goog.global);
        1608};
        1609
        1610
        \ No newline at end of file +base.js

        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 * @author arv@google.com (Erik Arvidsson)
        23 *
        24 * @provideGoog
        25 */
        26
        27
        28/**
        29 * @define {boolean} Overridden to true by the compiler when
        30 * --process_closure_primitives is specified.
        31 */
        32var COMPILED = false;
        33
        34
        35/**
        36 * Base namespace for the Closure library. Checks to see goog is already
        37 * defined in the current scope before assigning to prevent clobbering if
        38 * base.js is loaded more than once.
        39 *
        40 * @const
        41 */
        42var goog = goog || {};
        43
        44
        45/**
        46 * Reference to the global context. In most cases this will be 'window'.
        47 */
        48goog.global = this;
        49
        50
        51/**
        52 * A hook for overriding the define values in uncompiled mode.
        53 *
        54 * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before
        55 * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES},
        56 * {@code goog.define} will use the value instead of the default value. This
        57 * allows flags to be overwritten without compilation (this is normally
        58 * accomplished with the compiler's "define" flag).
        59 *
        60 * Example:
        61 * <pre>
        62 * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
        63 * </pre>
        64 *
        65 * @type {Object<string, (string|number|boolean)>|undefined}
        66 */
        67goog.global.CLOSURE_UNCOMPILED_DEFINES;
        68
        69
        70/**
        71 * A hook for overriding the define values in uncompiled or compiled mode,
        72 * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In
        73 * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.
        74 *
        75 * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or
        76 * string literals or the compiler will emit an error.
        77 *
        78 * While any @define value may be set, only those set with goog.define will be
        79 * effective for uncompiled code.
        80 *
        81 * Example:
        82 * <pre>
        83 * var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
        84 * </pre>
        85 *
        86 * @type {Object<string, (string|number|boolean)>|undefined}
        87 */
        88goog.global.CLOSURE_DEFINES;
        89
        90
        91/**
        92 * Returns true if the specified value is not undefined.
        93 * WARNING: Do not use this to test if an object has a property. Use the in
        94 * operator instead.
        95 *
        96 * @param {?} val Variable to test.
        97 * @return {boolean} Whether variable is defined.
        98 */
        99goog.isDef = function(val) {
        100 // void 0 always evaluates to undefined and hence we do not need to depend on
        101 // the definition of the global variable named 'undefined'.
        102 return val !== void 0;
        103};
        104
        105
        106/**
        107 * Builds an object structure for the provided namespace path, ensuring that
        108 * names that already exist are not overwritten. For example:
        109 * "a.b.c" -> a = {};a.b={};a.b.c={};
        110 * Used by goog.provide and goog.exportSymbol.
        111 * @param {string} name name of the object that this file defines.
        112 * @param {*=} opt_object the object to expose at the end of the path.
        113 * @param {Object=} opt_objectToExportTo The object to add the path to; default
        114 * is |goog.global|.
        115 * @private
        116 */
        117goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
        118 var parts = name.split('.');
        119 var cur = opt_objectToExportTo || goog.global;
        120
        121 // Internet Explorer exhibits strange behavior when throwing errors from
        122 // methods externed in this manner. See the testExportSymbolExceptions in
        123 // base_test.html for an example.
        124 if (!(parts[0] in cur) && cur.execScript) {
        125 cur.execScript('var ' + parts[0]);
        126 }
        127
        128 // Certain browsers cannot parse code in the form for((a in b); c;);
        129 // This pattern is produced by the JSCompiler when it collapses the
        130 // statement above into the conditional loop below. To prevent this from
        131 // happening, use a for-loop and reserve the init logic as below.
        132
        133 // Parentheses added to eliminate strict JS warning in Firefox.
        134 for (var part; parts.length && (part = parts.shift());) {
        135 if (!parts.length && goog.isDef(opt_object)) {
        136 // last part and we have an object; use it
        137 cur[part] = opt_object;
        138 } else if (cur[part]) {
        139 cur = cur[part];
        140 } else {
        141 cur = cur[part] = {};
        142 }
        143 }
        144};
        145
        146
        147/**
        148 * Defines a named value. In uncompiled mode, the value is retrieved from
        149 * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and
        150 * has the property specified, and otherwise used the defined defaultValue.
        151 * When compiled the default can be overridden using the compiler
        152 * options or the value set in the CLOSURE_DEFINES object.
        153 *
        154 * @param {string} name The distinguished name to provide.
        155 * @param {string|number|boolean} defaultValue
        156 */
        157goog.define = function(name, defaultValue) {
        158 var value = defaultValue;
        159 if (!COMPILED) {
        160 if (goog.global.CLOSURE_UNCOMPILED_DEFINES &&
        161 Object.prototype.hasOwnProperty.call(
        162 goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) {
        163 value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name];
        164 } else if (goog.global.CLOSURE_DEFINES &&
        165 Object.prototype.hasOwnProperty.call(
        166 goog.global.CLOSURE_DEFINES, name)) {
        167 value = goog.global.CLOSURE_DEFINES[name];
        168 }
        169 }
        170 goog.exportPath_(name, value);
        171};
        172
        173
        174/**
        175 * @define {boolean} DEBUG is provided as a convenience so that debugging code
        176 * that should not be included in a production js_binary can be easily stripped
        177 * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most
        178 * toString() methods should be declared inside an "if (goog.DEBUG)" conditional
        179 * because they are generally used for debugging purposes and it is difficult
        180 * for the JSCompiler to statically determine whether they are used.
        181 */
        182goog.define('goog.DEBUG', true);
        183
        184
        185/**
        186 * @define {string} LOCALE defines the locale being used for compilation. It is
        187 * used to select locale specific data to be compiled in js binary. BUILD rule
        188 * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler
        189 * option.
        190 *
        191 * Take into account that the locale code format is important. You should use
        192 * the canonical Unicode format with hyphen as a delimiter. Language must be
        193 * lowercase, Language Script - Capitalized, Region - UPPERCASE.
        194 * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
        195 *
        196 * See more info about locale codes here:
        197 * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
        198 *
        199 * For language codes you should use values defined by ISO 693-1. See it here
        200 * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
        201 * this rule: the Hebrew language. For legacy reasons the old code (iw) should
        202 * be used instead of the new code (he), see http://wiki/Main/IIISynonyms.
        203 */
        204goog.define('goog.LOCALE', 'en'); // default to en
        205
        206
        207/**
        208 * @define {boolean} Whether this code is running on trusted sites.
        209 *
        210 * On untrusted sites, several native functions can be defined or overridden by
        211 * external libraries like Prototype, Datejs, and JQuery and setting this flag
        212 * to false forces closure to use its own implementations when possible.
        213 *
        214 * If your JavaScript can be loaded by a third party site and you are wary about
        215 * relying on non-standard implementations, specify
        216 * "--define goog.TRUSTED_SITE=false" to the JSCompiler.
        217 */
        218goog.define('goog.TRUSTED_SITE', true);
        219
        220
        221/**
        222 * @define {boolean} Whether a project is expected to be running in strict mode.
        223 *
        224 * This define can be used to trigger alternate implementations compatible with
        225 * running in EcmaScript Strict mode or warn about unavailable functionality.
        226 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode
        227 *
        228 */
        229goog.define('goog.STRICT_MODE_COMPATIBLE', false);
        230
        231
        232/**
        233 * @define {boolean} Whether code that calls {@link goog.setTestOnly} should
        234 * be disallowed in the compilation unit.
        235 */
        236goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG);
        237
        238
        239/**
        240 * @define {boolean} Whether to use a Chrome app CSP-compliant method for
        241 * loading scripts via goog.require. @see appendScriptSrcNode_.
        242 */
        243goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false);
        244
        245
        246/**
        247 * Defines a namespace in Closure.
        248 *
        249 * A namespace may only be defined once in a codebase. It may be defined using
        250 * goog.provide() or goog.module().
        251 *
        252 * The presence of one or more goog.provide() calls in a file indicates
        253 * that the file defines the given objects/namespaces.
        254 * Provided symbols must not be null or undefined.
        255 *
        256 * In addition, goog.provide() creates the object stubs for a namespace
        257 * (for example, goog.provide("goog.foo.bar") will create the object
        258 * goog.foo.bar if it does not already exist).
        259 *
        260 * Build tools also scan for provide/require/module statements
        261 * to discern dependencies, build dependency files (see deps.js), etc.
        262 *
        263 * @see goog.require
        264 * @see goog.module
        265 * @param {string} name Namespace provided by this file in the form
        266 * "goog.package.part".
        267 */
        268goog.provide = function(name) {
        269 if (!COMPILED) {
        270 // Ensure that the same namespace isn't provided twice.
        271 // A goog.module/goog.provide maps a goog.require to a specific file
        272 if (goog.isProvided_(name)) {
        273 throw Error('Namespace "' + name + '" already declared.');
        274 }
        275 }
        276
        277 goog.constructNamespace_(name);
        278};
        279
        280
        281/**
        282 * @param {string} name Namespace provided by this file in the form
        283 * "goog.package.part".
        284 * @param {Object=} opt_obj The object to embed in the namespace.
        285 * @private
        286 */
        287goog.constructNamespace_ = function(name, opt_obj) {
        288 if (!COMPILED) {
        289 delete goog.implicitNamespaces_[name];
        290
        291 var namespace = name;
        292 while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
        293 if (goog.getObjectByName(namespace)) {
        294 break;
        295 }
        296 goog.implicitNamespaces_[namespace] = true;
        297 }
        298 }
        299
        300 goog.exportPath_(name, opt_obj);
        301};
        302
        303
        304/**
        305 * Module identifier validation regexp.
        306 * Note: This is a conservative check, it is very possible to be more lenient,
        307 * the primary exclusion here is "/" and "\" and a leading ".", these
        308 * restrictions are intended to leave the door open for using goog.require
        309 * with relative file paths rather than module identifiers.
        310 * @private
        311 */
        312goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/;
        313
        314
        315/**
        316 * Defines a module in Closure.
        317 *
        318 * Marks that this file must be loaded as a module and claims the namespace.
        319 *
        320 * A namespace may only be defined once in a codebase. It may be defined using
        321 * goog.provide() or goog.module().
        322 *
        323 * goog.module() has three requirements:
        324 * - goog.module may not be used in the same file as goog.provide.
        325 * - goog.module must be the first statement in the file.
        326 * - only one goog.module is allowed per file.
        327 *
        328 * When a goog.module annotated file is loaded, it is enclosed in
        329 * a strict function closure. This means that:
        330 * - any variables declared in a goog.module file are private to the file
        331 * (not global), though the compiler is expected to inline the module.
        332 * - The code must obey all the rules of "strict" JavaScript.
        333 * - the file will be marked as "use strict"
        334 *
        335 * NOTE: unlike goog.provide, goog.module does not declare any symbols by
        336 * itself. If declared symbols are desired, use
        337 * goog.module.declareLegacyNamespace().
        338 *
        339 *
        340 * See the public goog.module proposal: http://goo.gl/Va1hin
        341 *
        342 * @param {string} name Namespace provided by this file in the form
        343 * "goog.package.part", is expected but not required.
        344 */
        345goog.module = function(name) {
        346 if (!goog.isString(name) ||
        347 !name ||
        348 name.search(goog.VALID_MODULE_RE_) == -1) {
        349 throw Error('Invalid module identifier');
        350 }
        351 if (!goog.isInModuleLoader_()) {
        352 throw Error('Module ' + name + ' has been loaded incorrectly.');
        353 }
        354 if (goog.moduleLoaderState_.moduleName) {
        355 throw Error('goog.module may only be called once per module.');
        356 }
        357
        358 // Store the module name for the loader.
        359 goog.moduleLoaderState_.moduleName = name;
        360 if (!COMPILED) {
        361 // Ensure that the same namespace isn't provided twice.
        362 // A goog.module/goog.provide maps a goog.require to a specific file
        363 if (goog.isProvided_(name)) {
        364 throw Error('Namespace "' + name + '" already declared.');
        365 }
        366 delete goog.implicitNamespaces_[name];
        367 }
        368};
        369
        370
        371/**
        372 * @param {string} name The module identifier.
        373 * @return {?} The module exports for an already loaded module or null.
        374 *
        375 * Note: This is not an alternative to goog.require, it does not
        376 * indicate a hard dependency, instead it is used to indicate
        377 * an optional dependency or to access the exports of a module
        378 * that has already been loaded.
        379 * @suppress {missingProvide}
        380 */
        381goog.module.get = function(name) {
        382 return goog.module.getInternal_(name);
        383};
        384
        385
        386/**
        387 * @param {string} name The module identifier.
        388 * @return {?} The module exports for an already loaded module or null.
        389 * @private
        390 */
        391goog.module.getInternal_ = function(name) {
        392 if (!COMPILED) {
        393 if (goog.isProvided_(name)) {
        394 // goog.require only return a value with-in goog.module files.
        395 return name in goog.loadedModules_ ?
        396 goog.loadedModules_[name] :
        397 goog.getObjectByName(name);
        398 } else {
        399 return null;
        400 }
        401 }
        402};
        403
        404
        405/**
        406 * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}}
        407 */
        408goog.moduleLoaderState_ = null;
        409
        410
        411/**
        412 * @private
        413 * @return {boolean} Whether a goog.module is currently being initialized.
        414 */
        415goog.isInModuleLoader_ = function() {
        416 return goog.moduleLoaderState_ != null;
        417};
        418
        419
        420/**
        421 * Provide the module's exports as a globally accessible object under the
        422 * module's declared name. This is intended to ease migration to goog.module
        423 * for files that have existing usages.
        424 * @suppress {missingProvide}
        425 */
        426goog.module.declareLegacyNamespace = function() {
        427 if (!COMPILED && !goog.isInModuleLoader_()) {
        428 throw new Error('goog.module.declareLegacyNamespace must be called from ' +
        429 'within a goog.module');
        430 }
        431 if (!COMPILED && !goog.moduleLoaderState_.moduleName) {
        432 throw Error('goog.module must be called prior to ' +
        433 'goog.module.declareLegacyNamespace.');
        434 }
        435 goog.moduleLoaderState_.declareLegacyNamespace = true;
        436};
        437
        438
        439/**
        440 * Marks that the current file should only be used for testing, and never for
        441 * live code in production.
        442 *
        443 * In the case of unit tests, the message may optionally be an exact namespace
        444 * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
        445 * provide (if not explicitly defined in the code).
        446 *
        447 * @param {string=} opt_message Optional message to add to the error that's
        448 * raised when used in production code.
        449 */
        450goog.setTestOnly = function(opt_message) {
        451 if (goog.DISALLOW_TEST_ONLY_CODE) {
        452 opt_message = opt_message || '';
        453 throw Error('Importing test-only code into non-debug environment' +
        454 (opt_message ? ': ' + opt_message : '.'));
        455 }
        456};
        457
        458
        459/**
        460 * Forward declares a symbol. This is an indication to the compiler that the
        461 * symbol may be used in the source yet is not required and may not be provided
        462 * in compilation.
        463 *
        464 * The most common usage of forward declaration is code that takes a type as a
        465 * function parameter but does not need to require it. By forward declaring
        466 * instead of requiring, no hard dependency is made, and (if not required
        467 * elsewhere) the namespace may never be required and thus, not be pulled
        468 * into the JavaScript binary. If it is required elsewhere, it will be type
        469 * checked as normal.
        470 *
        471 *
        472 * @param {string} name The namespace to forward declare in the form of
        473 * "goog.package.part".
        474 */
        475goog.forwardDeclare = function(name) {};
        476
        477
        478/**
        479 * Forward declare type information. Used to assign types to goog.global
        480 * referenced object that would otherwise result in unknown type references
        481 * and thus block property disambiguation.
        482 */
        483goog.forwardDeclare('Document');
        484goog.forwardDeclare('HTMLScriptElement');
        485goog.forwardDeclare('XMLHttpRequest');
        486
        487
        488if (!COMPILED) {
        489
        490 /**
        491 * Check if the given name has been goog.provided. This will return false for
        492 * names that are available only as implicit namespaces.
        493 * @param {string} name name of the object to look for.
        494 * @return {boolean} Whether the name has been provided.
        495 * @private
        496 */
        497 goog.isProvided_ = function(name) {
        498 return (name in goog.loadedModules_) ||
        499 (!goog.implicitNamespaces_[name] &&
        500 goog.isDefAndNotNull(goog.getObjectByName(name)));
        501 };
        502
        503 /**
        504 * Namespaces implicitly defined by goog.provide. For example,
        505 * goog.provide('goog.events.Event') implicitly declares that 'goog' and
        506 * 'goog.events' must be namespaces.
        507 *
        508 * @type {!Object<string, (boolean|undefined)>}
        509 * @private
        510 */
        511 goog.implicitNamespaces_ = {'goog.module': true};
        512
        513 // NOTE: We add goog.module as an implicit namespace as goog.module is defined
        514 // here and because the existing module package has not been moved yet out of
        515 // the goog.module namespace. This satisifies both the debug loader and
        516 // ahead-of-time dependency management.
        517}
        518
        519
        520/**
        521 * Returns an object based on its fully qualified external name. The object
        522 * is not found if null or undefined. If you are using a compilation pass that
        523 * renames property names beware that using this function will not find renamed
        524 * properties.
        525 *
        526 * @param {string} name The fully qualified name.
        527 * @param {Object=} opt_obj The object within which to look; default is
        528 * |goog.global|.
        529 * @return {?} The value (object or primitive) or, if not found, null.
        530 */
        531goog.getObjectByName = function(name, opt_obj) {
        532 var parts = name.split('.');
        533 var cur = opt_obj || goog.global;
        534 for (var part; part = parts.shift(); ) {
        535 if (goog.isDefAndNotNull(cur[part])) {
        536 cur = cur[part];
        537 } else {
        538 return null;
        539 }
        540 }
        541 return cur;
        542};
        543
        544
        545/**
        546 * Globalizes a whole namespace, such as goog or goog.lang.
        547 *
        548 * @param {!Object} obj The namespace to globalize.
        549 * @param {Object=} opt_global The object to add the properties to.
        550 * @deprecated Properties may be explicitly exported to the global scope, but
        551 * this should no longer be done in bulk.
        552 */
        553goog.globalize = function(obj, opt_global) {
        554 var global = opt_global || goog.global;
        555 for (var x in obj) {
        556 global[x] = obj[x];
        557 }
        558};
        559
        560
        561/**
        562 * Adds a dependency from a file to the files it requires.
        563 * @param {string} relPath The path to the js file.
        564 * @param {!Array<string>} provides An array of strings with
        565 * the names of the objects this file provides.
        566 * @param {!Array<string>} requires An array of strings with
        567 * the names of the objects this file requires.
        568 * @param {boolean=} opt_isModule Whether this dependency must be loaded as
        569 * a module as declared by goog.module.
        570 */
        571goog.addDependency = function(relPath, provides, requires, opt_isModule) {
        572 if (goog.DEPENDENCIES_ENABLED) {
        573 var provide, require;
        574 var path = relPath.replace(/\\/g, '/');
        575 var deps = goog.dependencies_;
        576 for (var i = 0; provide = provides[i]; i++) {
        577 deps.nameToPath[provide] = path;
        578 deps.pathIsModule[path] = !!opt_isModule;
        579 }
        580 for (var j = 0; require = requires[j]; j++) {
        581 if (!(path in deps.requires)) {
        582 deps.requires[path] = {};
        583 }
        584 deps.requires[path][require] = true;
        585 }
        586 }
        587};
        588
        589
        590
        591
        592// NOTE(nnaze): The debug DOM loader was included in base.js as an original way
        593// to do "debug-mode" development. The dependency system can sometimes be
        594// confusing, as can the debug DOM loader's asynchronous nature.
        595//
        596// With the DOM loader, a call to goog.require() is not blocking -- the script
        597// will not load until some point after the current script. If a namespace is
        598// needed at runtime, it needs to be defined in a previous script, or loaded via
        599// require() with its registered dependencies.
        600// User-defined namespaces may need their own deps file. See http://go/js_deps,
        601// http://go/genjsdeps, or, externally, DepsWriter.
        602// https://developers.google.com/closure/library/docs/depswriter
        603//
        604// Because of legacy clients, the DOM loader can't be easily removed from
        605// base.js. Work is being done to make it disableable or replaceable for
        606// different environments (DOM-less JavaScript interpreters like Rhino or V8,
        607// for example). See bootstrap/ for more information.
        608
        609
        610/**
        611 * @define {boolean} Whether to enable the debug loader.
        612 *
        613 * If enabled, a call to goog.require() will attempt to load the namespace by
        614 * appending a script tag to the DOM (if the namespace has been registered).
        615 *
        616 * If disabled, goog.require() will simply assert that the namespace has been
        617 * provided (and depend on the fact that some outside tool correctly ordered
        618 * the script).
        619 */
        620goog.define('goog.ENABLE_DEBUG_LOADER', true);
        621
        622
        623/**
        624 * @param {string} msg
        625 * @private
        626 */
        627goog.logToConsole_ = function(msg) {
        628 if (goog.global.console) {
        629 goog.global.console['error'](msg);
        630 }
        631};
        632
        633
        634/**
        635 * Implements a system for the dynamic resolution of dependencies that works in
        636 * parallel with the BUILD system. Note that all calls to goog.require will be
        637 * stripped by the JSCompiler when the --process_closure_primitives option is
        638 * used.
        639 * @see goog.provide
        640 * @param {string} name Namespace to include (as was given in goog.provide()) in
        641 * the form "goog.package.part".
        642 * @return {?} If called within a goog.module file, the associated namespace or
        643 * module otherwise null.
        644 */
        645goog.require = function(name) {
        646 // If the object already exists we do not need do do anything.
        647 if (!COMPILED) {
        648 if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) {
        649 goog.maybeProcessDeferredDep_(name);
        650 }
        651
        652 if (goog.isProvided_(name)) {
        653 if (goog.isInModuleLoader_()) {
        654 return goog.module.getInternal_(name);
        655 } else {
        656 return null;
        657 }
        658 }
        659
        660 if (goog.ENABLE_DEBUG_LOADER) {
        661 var path = goog.getPathFromDeps_(name);
        662 if (path) {
        663 goog.writeScripts_(path);
        664 return null;
        665 }
        666 }
        667
        668 var errorMessage = 'goog.require could not find: ' + name;
        669 goog.logToConsole_(errorMessage);
        670
        671 throw Error(errorMessage);
        672 }
        673};
        674
        675
        676/**
        677 * Path for included scripts.
        678 * @type {string}
        679 */
        680goog.basePath = '';
        681
        682
        683/**
        684 * A hook for overriding the base path.
        685 * @type {string|undefined}
        686 */
        687goog.global.CLOSURE_BASE_PATH;
        688
        689
        690/**
        691 * Whether to write out Closure's deps file. By default, the deps are written.
        692 * @type {boolean|undefined}
        693 */
        694goog.global.CLOSURE_NO_DEPS;
        695
        696
        697/**
        698 * A function to import a single script. This is meant to be overridden when
        699 * Closure is being run in non-HTML contexts, such as web workers. It's defined
        700 * in the global scope so that it can be set before base.js is loaded, which
        701 * allows deps.js to be imported properly.
        702 *
        703 * The function is passed the script source, which is a relative URI. It should
        704 * return true if the script was imported, false otherwise.
        705 * @type {(function(string): boolean)|undefined}
        706 */
        707goog.global.CLOSURE_IMPORT_SCRIPT;
        708
        709
        710/**
        711 * Null function used for default values of callbacks, etc.
        712 * @return {void} Nothing.
        713 */
        714goog.nullFunction = function() {};
        715
        716
        717/**
        718 * When defining a class Foo with an abstract method bar(), you can do:
        719 * Foo.prototype.bar = goog.abstractMethod
        720 *
        721 * Now if a subclass of Foo fails to override bar(), an error will be thrown
        722 * when bar() is invoked.
        723 *
        724 * Note: This does not take the name of the function to override as an argument
        725 * because that would make it more difficult to obfuscate our JavaScript code.
        726 *
        727 * @type {!Function}
        728 * @throws {Error} when invoked to indicate the method should be overridden.
        729 */
        730goog.abstractMethod = function() {
        731 throw Error('unimplemented abstract method');
        732};
        733
        734
        735/**
        736 * Adds a {@code getInstance} static method that always returns the same
        737 * instance object.
        738 * @param {!Function} ctor The constructor for the class to add the static
        739 * method to.
        740 */
        741goog.addSingletonGetter = function(ctor) {
        742 ctor.getInstance = function() {
        743 if (ctor.instance_) {
        744 return ctor.instance_;
        745 }
        746 if (goog.DEBUG) {
        747 // NOTE: JSCompiler can't optimize away Array#push.
        748 goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
        749 }
        750 return ctor.instance_ = new ctor;
        751 };
        752};
        753
        754
        755/**
        756 * All singleton classes that have been instantiated, for testing. Don't read
        757 * it directly, use the {@code goog.testing.singleton} module. The compiler
        758 * removes this variable if unused.
        759 * @type {!Array<!Function>}
        760 * @private
        761 */
        762goog.instantiatedSingletons_ = [];
        763
        764
        765/**
        766 * @define {boolean} Whether to load goog.modules using {@code eval} when using
        767 * the debug loader. This provides a better debugging experience as the
        768 * source is unmodified and can be edited using Chrome Workspaces or similar.
        769 * However in some environments the use of {@code eval} is banned
        770 * so we provide an alternative.
        771 */
        772goog.define('goog.LOAD_MODULE_USING_EVAL', true);
        773
        774
        775/**
        776 * @define {boolean} Whether the exports of goog.modules should be sealed when
        777 * possible.
        778 */
        779goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);
        780
        781
        782/**
        783 * The registry of initialized modules:
        784 * the module identifier to module exports map.
        785 * @private @const {!Object<string, ?>}
        786 */
        787goog.loadedModules_ = {};
        788
        789
        790/**
        791 * True if goog.dependencies_ is available.
        792 * @const {boolean}
        793 */
        794goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
        795
        796
        797if (goog.DEPENDENCIES_ENABLED) {
        798
        799 /**
        800 * This object is used to keep track of dependencies and other data that is
        801 * used for loading scripts.
        802 * @private
        803 * @type {{
        804 * pathIsModule: !Object<string, boolean>,
        805 * nameToPath: !Object<string, string>,
        806 * requires: !Object<string, !Object<string, boolean>>,
        807 * visited: !Object<string, boolean>,
        808 * written: !Object<string, boolean>,
        809 * deferred: !Object<string, string>
        810 * }}
        811 */
        812 goog.dependencies_ = {
        813 pathIsModule: {}, // 1 to 1
        814
        815 nameToPath: {}, // 1 to 1
        816
        817 requires: {}, // 1 to many
        818
        819 // Used when resolving dependencies to prevent us from visiting file twice.
        820 visited: {},
        821
        822 written: {}, // Used to keep track of script files we have written.
        823
        824 deferred: {} // Used to track deferred module evaluations in old IEs
        825 };
        826
        827
        828 /**
        829 * Tries to detect whether is in the context of an HTML document.
        830 * @return {boolean} True if it looks like HTML document.
        831 * @private
        832 */
        833 goog.inHtmlDocument_ = function() {
        834 /** @type {Document} */
        835 var doc = goog.global.document;
        836 return doc != null && 'write' in doc; // XULDocument misses write.
        837 };
        838
        839
        840 /**
        841 * Tries to detect the base path of base.js script that bootstraps Closure.
        842 * @private
        843 */
        844 goog.findBasePath_ = function() {
        845 if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) {
        846 goog.basePath = goog.global.CLOSURE_BASE_PATH;
        847 return;
        848 } else if (!goog.inHtmlDocument_()) {
        849 return;
        850 }
        851 /** @type {Document} */
        852 var doc = goog.global.document;
        853 var scripts = doc.getElementsByTagName('SCRIPT');
        854 // Search backwards since the current script is in almost all cases the one
        855 // that has base.js.
        856 for (var i = scripts.length - 1; i >= 0; --i) {
        857 var script = /** @type {!HTMLScriptElement} */ (scripts[i]);
        858 var src = script.src;
        859 var qmark = src.lastIndexOf('?');
        860 var l = qmark == -1 ? src.length : qmark;
        861 if (src.substr(l - 7, 7) == 'base.js') {
        862 goog.basePath = src.substr(0, l - 7);
        863 return;
        864 }
        865 }
        866 };
        867
        868
        869 /**
        870 * Imports a script if, and only if, that script hasn't already been imported.
        871 * (Must be called at execution time)
        872 * @param {string} src Script source.
        873 * @param {string=} opt_sourceText The optionally source text to evaluate
        874 * @private
        875 */
        876 goog.importScript_ = function(src, opt_sourceText) {
        877 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
        878 goog.writeScriptTag_;
        879 if (importScript(src, opt_sourceText)) {
        880 goog.dependencies_.written[src] = true;
        881 }
        882 };
        883
        884
        885 /** @const @private {boolean} */
        886 goog.IS_OLD_IE_ = !!(!goog.global.atob && goog.global.document &&
        887 goog.global.document.all);
        888
        889
        890 /**
        891 * Given a URL initiate retrieval and execution of the module.
        892 * @param {string} src Script source URL.
        893 * @private
        894 */
        895 goog.importModule_ = function(src) {
        896 // In an attempt to keep browsers from timing out loading scripts using
        897 // synchronous XHRs, put each load in its own script block.
        898 var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");';
        899
        900 if (goog.importScript_('', bootstrap)) {
        901 goog.dependencies_.written[src] = true;
        902 }
        903 };
        904
        905
        906 /** @private {!Array<string>} */
        907 goog.queuedModules_ = [];
        908
        909
        910 /**
        911 * Return an appropriate module text. Suitable to insert into
        912 * a script tag (that is unescaped).
        913 * @param {string} srcUrl
        914 * @param {string} scriptText
        915 * @return {string}
        916 * @private
        917 */
        918 goog.wrapModule_ = function(srcUrl, scriptText) {
        919 if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) {
        920 return '' +
        921 'goog.loadModule(function(exports) {' +
        922 '"use strict";' +
        923 scriptText +
        924 '\n' + // terminate any trailing single line comment.
        925 ';return exports' +
        926 '});' +
        927 '\n//# sourceURL=' + srcUrl + '\n';
        928 } else {
        929 return '' +
        930 'goog.loadModule(' +
        931 goog.global.JSON.stringify(
        932 scriptText + '\n//# sourceURL=' + srcUrl + '\n') +
        933 ');';
        934 }
        935 };
        936
        937 // On IE9 and earlier, it is necessary to handle
        938 // deferred module loads. In later browsers, the
        939 // code to be evaluated is simply inserted as a script
        940 // block in the correct order. To eval deferred
        941 // code at the right time, we piggy back on goog.require to call
        942 // goog.maybeProcessDeferredDep_.
        943 //
        944 // The goog.requires are used both to bootstrap
        945 // the loading process (when no deps are available) and
        946 // declare that they should be available.
        947 //
        948 // Here we eval the sources, if all the deps are available
        949 // either already eval'd or goog.require'd. This will
        950 // be the case when all the dependencies have already
        951 // been loaded, and the dependent module is loaded.
        952 //
        953 // But this alone isn't sufficient because it is also
        954 // necessary to handle the case where there is no root
        955 // that is not deferred. For that there we register for an event
        956 // and trigger goog.loadQueuedModules_ handle any remaining deferred
        957 // evaluations.
        958
        959 /**
        960 * Handle any remaining deferred goog.module evals.
        961 * @private
        962 */
        963 goog.loadQueuedModules_ = function() {
        964 var count = goog.queuedModules_.length;
        965 if (count > 0) {
        966 var queue = goog.queuedModules_;
        967 goog.queuedModules_ = [];
        968 for (var i = 0; i < count; i++) {
        969 var path = queue[i];
        970 goog.maybeProcessDeferredPath_(path);
        971 }
        972 }
        973 };
        974
        975
        976 /**
        977 * Eval the named module if its dependencies are
        978 * available.
        979 * @param {string} name The module to load.
        980 * @private
        981 */
        982 goog.maybeProcessDeferredDep_ = function(name) {
        983 if (goog.isDeferredModule_(name) &&
        984 goog.allDepsAreAvailable_(name)) {
        985 var path = goog.getPathFromDeps_(name);
        986 goog.maybeProcessDeferredPath_(goog.basePath + path);
        987 }
        988 };
        989
        990 /**
        991 * @param {string} name The module to check.
        992 * @return {boolean} Whether the name represents a
        993 * module whose evaluation has been deferred.
        994 * @private
        995 */
        996 goog.isDeferredModule_ = function(name) {
        997 var path = goog.getPathFromDeps_(name);
        998 if (path && goog.dependencies_.pathIsModule[path]) {
        999 var abspath = goog.basePath + path;
        1000 return (abspath) in goog.dependencies_.deferred;
        1001 }
        1002 return false;
        1003 };
        1004
        1005 /**
        1006 * @param {string} name The module to check.
        1007 * @return {boolean} Whether the name represents a
        1008 * module whose declared dependencies have all been loaded
        1009 * (eval'd or a deferred module load)
        1010 * @private
        1011 */
        1012 goog.allDepsAreAvailable_ = function(name) {
        1013 var path = goog.getPathFromDeps_(name);
        1014 if (path && (path in goog.dependencies_.requires)) {
        1015 for (var requireName in goog.dependencies_.requires[path]) {
        1016 if (!goog.isProvided_(requireName) &&
        1017 !goog.isDeferredModule_(requireName)) {
        1018 return false;
        1019 }
        1020 }
        1021 }
        1022 return true;
        1023 };
        1024
        1025
        1026 /**
        1027 * @param {string} abspath
        1028 * @private
        1029 */
        1030 goog.maybeProcessDeferredPath_ = function(abspath) {
        1031 if (abspath in goog.dependencies_.deferred) {
        1032 var src = goog.dependencies_.deferred[abspath];
        1033 delete goog.dependencies_.deferred[abspath];
        1034 goog.globalEval(src);
        1035 }
        1036 };
        1037
        1038
        1039 /**
        1040 * Load a goog.module from the provided URL. This is not a general purpose
        1041 * code loader and does not support late loading code, that is it should only
        1042 * be used during page load. This method exists to support unit tests and
        1043 * "debug" loaders that would otherwise have inserted script tags. Under the
        1044 * hood this needs to use a synchronous XHR and is not recommeneded for
        1045 * production code.
        1046 *
        1047 * The module's goog.requires must have already been satisified; an exception
        1048 * will be thrown if this is not the case. This assumption is that no
        1049 * "deps.js" file exists, so there is no way to discover and locate the
        1050 * module-to-be-loaded's dependencies and no attempt is made to do so.
        1051 *
        1052 * There should only be one attempt to load a module. If
        1053 * "goog.loadModuleFromUrl" is called for an already loaded module, an
        1054 * exception will be throw.
        1055 *
        1056 * @param {string} url The URL from which to attempt to load the goog.module.
        1057 */
        1058 goog.loadModuleFromUrl = function(url) {
        1059 // Because this executes synchronously, we don't need to do any additional
        1060 // bookkeeping. When "goog.loadModule" the namespace will be marked as
        1061 // having been provided which is sufficient.
        1062 goog.retrieveAndExecModule_(url);
        1063 };
        1064
        1065
        1066 /**
        1067 * @param {function(?):?|string} moduleDef The module definition.
        1068 */
        1069 goog.loadModule = function(moduleDef) {
        1070 // NOTE: we allow function definitions to be either in the from
        1071 // of a string to eval (which keeps the original source intact) or
        1072 // in a eval forbidden environment (CSP) we allow a function definition
        1073 // which in its body must call {@code goog.module}, and return the exports
        1074 // of the module.
        1075 var previousState = goog.moduleLoaderState_;
        1076 try {
        1077 goog.moduleLoaderState_ = {
        1078 moduleName: undefined,
        1079 declareLegacyNamespace: false
        1080 };
        1081 var exports;
        1082 if (goog.isFunction(moduleDef)) {
        1083 exports = moduleDef.call(goog.global, {});
        1084 } else if (goog.isString(moduleDef)) {
        1085 exports = goog.loadModuleFromSource_.call(goog.global, moduleDef);
        1086 } else {
        1087 throw Error('Invalid module definition');
        1088 }
        1089
        1090 var moduleName = goog.moduleLoaderState_.moduleName;
        1091 if (!goog.isString(moduleName) || !moduleName) {
        1092 throw Error('Invalid module name \"' + moduleName + '\"');
        1093 }
        1094
        1095 // Don't seal legacy namespaces as they may be uses as a parent of
        1096 // another namespace
        1097 if (goog.moduleLoaderState_.declareLegacyNamespace) {
        1098 goog.constructNamespace_(moduleName, exports);
        1099 } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) {
        1100 Object.seal(exports);
        1101 }
        1102
        1103 goog.loadedModules_[moduleName] = exports;
        1104 } finally {
        1105 goog.moduleLoaderState_ = previousState;
        1106 }
        1107 };
        1108
        1109
        1110 /**
        1111 * @private @const {function(string):?}
        1112 *
        1113 * The new type inference warns because this function has no formal
        1114 * parameters, but its jsdoc says that it takes one argument.
        1115 * (The argument is used via arguments[0], but NTI does not detect this.)
        1116 * @suppress {newCheckTypes}
        1117 */
        1118 goog.loadModuleFromSource_ = function() {
        1119 // NOTE: we avoid declaring parameters or local variables here to avoid
        1120 // masking globals or leaking values into the module definition.
        1121 'use strict';
        1122 var exports = {};
        1123 eval(arguments[0]);
        1124 return exports;
        1125 };
        1126
        1127
        1128 /**
        1129 * Writes a new script pointing to {@code src} directly into the DOM.
        1130 *
        1131 * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for
        1132 * the fallback mechanism.
        1133 *
        1134 * @param {string} src The script URL.
        1135 * @private
        1136 */
        1137 goog.writeScriptSrcNode_ = function(src) {
        1138 goog.global.document.write(
        1139 '<script type="text/javascript" src="' + src + '"></' + 'script>');
        1140 };
        1141
        1142
        1143 /**
        1144 * Appends a new script node to the DOM using a CSP-compliant mechanism. This
        1145 * method exists as a fallback for document.write (which is not allowed in a
        1146 * strict CSP context, e.g., Chrome apps).
        1147 *
        1148 * NOTE: This method is not analogous to using document.write to insert a
        1149 * <script> tag; specifically, the user agent will execute a script added by
        1150 * document.write immediately after the current script block finishes
        1151 * executing, whereas the DOM-appended script node will not be executed until
        1152 * the entire document is parsed and executed. That is to say, this script is
        1153 * added to the end of the script execution queue.
        1154 *
        1155 * The page must not attempt to call goog.required entities until after the
        1156 * document has loaded, e.g., in or after the window.onload callback.
        1157 *
        1158 * @param {string} src The script URL.
        1159 * @private
        1160 */
        1161 goog.appendScriptSrcNode_ = function(src) {
        1162 /** @type {Document} */
        1163 var doc = goog.global.document;
        1164 var scriptEl = /** @type {HTMLScriptElement} */ (
        1165 doc.createElement('script'));
        1166 scriptEl.type = 'text/javascript';
        1167 scriptEl.src = src;
        1168 scriptEl.defer = false;
        1169 scriptEl.async = false;
        1170 doc.head.appendChild(scriptEl);
        1171 };
        1172
        1173
        1174 /**
        1175 * The default implementation of the import function. Writes a script tag to
        1176 * import the script.
        1177 *
        1178 * @param {string} src The script url.
        1179 * @param {string=} opt_sourceText The optionally source text to evaluate
        1180 * @return {boolean} True if the script was imported, false otherwise.
        1181 * @private
        1182 */
        1183 goog.writeScriptTag_ = function(src, opt_sourceText) {
        1184 if (goog.inHtmlDocument_()) {
        1185 /** @type {Document} */
        1186 var doc = goog.global.document;
        1187
        1188 // If the user tries to require a new symbol after document load,
        1189 // something has gone terribly wrong. Doing a document.write would
        1190 // wipe out the page. This does not apply to the CSP-compliant method
        1191 // of writing script tags.
        1192 if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&
        1193 doc.readyState == 'complete') {
        1194 // Certain test frameworks load base.js multiple times, which tries
        1195 // to write deps.js each time. If that happens, just fail silently.
        1196 // These frameworks wipe the page between each load of base.js, so this
        1197 // is OK.
        1198 var isDeps = /\bdeps.js$/.test(src);
        1199 if (isDeps) {
        1200 return false;
        1201 } else {
        1202 throw Error('Cannot write "' + src + '" after document load');
        1203 }
        1204 }
        1205
        1206 var isOldIE = goog.IS_OLD_IE_;
        1207
        1208 if (opt_sourceText === undefined) {
        1209 if (!isOldIE) {
        1210 if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {
        1211 goog.appendScriptSrcNode_(src);
        1212 } else {
        1213 goog.writeScriptSrcNode_(src);
        1214 }
        1215 } else {
        1216 var state = " onreadystatechange='goog.onScriptLoad_(this, " +
        1217 ++goog.lastNonModuleScriptIndex_ + ")' ";
        1218 doc.write(
        1219 '<script type="text/javascript" src="' +
        1220 src + '"' + state + '></' + 'script>');
        1221 }
        1222 } else {
        1223 doc.write(
        1224 '<script type="text/javascript">' +
        1225 opt_sourceText +
        1226 '</' + 'script>');
        1227 }
        1228 return true;
        1229 } else {
        1230 return false;
        1231 }
        1232 };
        1233
        1234
        1235 /** @private {number} */
        1236 goog.lastNonModuleScriptIndex_ = 0;
        1237
        1238
        1239 /**
        1240 * A readystatechange handler for legacy IE
        1241 * @param {!HTMLScriptElement} script
        1242 * @param {number} scriptIndex
        1243 * @return {boolean}
        1244 * @private
        1245 */
        1246 goog.onScriptLoad_ = function(script, scriptIndex) {
        1247 // for now load the modules when we reach the last script,
        1248 // later allow more inter-mingling.
        1249 if (script.readyState == 'complete' &&
        1250 goog.lastNonModuleScriptIndex_ == scriptIndex) {
        1251 goog.loadQueuedModules_();
        1252 }
        1253 return true;
        1254 };
        1255
        1256 /**
        1257 * Resolves dependencies based on the dependencies added using addDependency
        1258 * and calls importScript_ in the correct order.
        1259 * @param {string} pathToLoad The path from which to start discovering
        1260 * dependencies.
        1261 * @private
        1262 */
        1263 goog.writeScripts_ = function(pathToLoad) {
        1264 /** @type {!Array<string>} The scripts we need to write this time. */
        1265 var scripts = [];
        1266 var seenScript = {};
        1267 var deps = goog.dependencies_;
        1268
        1269 /** @param {string} path */
        1270 function visitNode(path) {
        1271 if (path in deps.written) {
        1272 return;
        1273 }
        1274
        1275 // We have already visited this one. We can get here if we have cyclic
        1276 // dependencies.
        1277 if (path in deps.visited) {
        1278 return;
        1279 }
        1280
        1281 deps.visited[path] = true;
        1282
        1283 if (path in deps.requires) {
        1284 for (var requireName in deps.requires[path]) {
        1285 // If the required name is defined, we assume that it was already
        1286 // bootstrapped by other means.
        1287 if (!goog.isProvided_(requireName)) {
        1288 if (requireName in deps.nameToPath) {
        1289 visitNode(deps.nameToPath[requireName]);
        1290 } else {
        1291 throw Error('Undefined nameToPath for ' + requireName);
        1292 }
        1293 }
        1294 }
        1295 }
        1296
        1297 if (!(path in seenScript)) {
        1298 seenScript[path] = true;
        1299 scripts.push(path);
        1300 }
        1301 }
        1302
        1303 visitNode(pathToLoad);
        1304
        1305 // record that we are going to load all these scripts.
        1306 for (var i = 0; i < scripts.length; i++) {
        1307 var path = scripts[i];
        1308 goog.dependencies_.written[path] = true;
        1309 }
        1310
        1311 // If a module is loaded synchronously then we need to
        1312 // clear the current inModuleLoader value, and restore it when we are
        1313 // done loading the current "requires".
        1314 var moduleState = goog.moduleLoaderState_;
        1315 goog.moduleLoaderState_ = null;
        1316
        1317 for (var i = 0; i < scripts.length; i++) {
        1318 var path = scripts[i];
        1319 if (path) {
        1320 if (!deps.pathIsModule[path]) {
        1321 goog.importScript_(goog.basePath + path);
        1322 } else {
        1323 goog.importModule_(goog.basePath + path);
        1324 }
        1325 } else {
        1326 goog.moduleLoaderState_ = moduleState;
        1327 throw Error('Undefined script input');
        1328 }
        1329 }
        1330
        1331 // restore the current "module loading state"
        1332 goog.moduleLoaderState_ = moduleState;
        1333 };
        1334
        1335
        1336 /**
        1337 * Looks at the dependency rules and tries to determine the script file that
        1338 * fulfills a particular rule.
        1339 * @param {string} rule In the form goog.namespace.Class or project.script.
        1340 * @return {?string} Url corresponding to the rule, or null.
        1341 * @private
        1342 */
        1343 goog.getPathFromDeps_ = function(rule) {
        1344 if (rule in goog.dependencies_.nameToPath) {
        1345 return goog.dependencies_.nameToPath[rule];
        1346 } else {
        1347 return null;
        1348 }
        1349 };
        1350
        1351 goog.findBasePath_();
        1352
        1353 // Allow projects to manage the deps files themselves.
        1354 if (!goog.global.CLOSURE_NO_DEPS) {
        1355 goog.importScript_(goog.basePath + 'deps.js');
        1356 }
        1357}
        1358
        1359
        1360/**
        1361 * Normalize a file path by removing redundant ".." and extraneous "." file
        1362 * path components.
        1363 * @param {string} path
        1364 * @return {string}
        1365 * @private
        1366 */
        1367goog.normalizePath_ = function(path) {
        1368 var components = path.split('/');
        1369 var i = 0;
        1370 while (i < components.length) {
        1371 if (components[i] == '.') {
        1372 components.splice(i, 1);
        1373 } else if (i && components[i] == '..' &&
        1374 components[i - 1] && components[i - 1] != '..') {
        1375 components.splice(--i, 2);
        1376 } else {
        1377 i++;
        1378 }
        1379 }
        1380 return components.join('/');
        1381};
        1382
        1383
        1384/**
        1385 * Loads file by synchronous XHR. Should not be used in production environments.
        1386 * @param {string} src Source URL.
        1387 * @return {string} File contents.
        1388 * @private
        1389 */
        1390goog.loadFileSync_ = function(src) {
        1391 if (goog.global.CLOSURE_LOAD_FILE_SYNC) {
        1392 return goog.global.CLOSURE_LOAD_FILE_SYNC(src);
        1393 } else {
        1394 /** @type {XMLHttpRequest} */
        1395 var xhr = new goog.global['XMLHttpRequest']();
        1396 xhr.open('get', src, false);
        1397 xhr.send();
        1398 return xhr.responseText;
        1399 }
        1400};
        1401
        1402
        1403/**
        1404 * Retrieve and execute a module.
        1405 * @param {string} src Script source URL.
        1406 * @private
        1407 */
        1408goog.retrieveAndExecModule_ = function(src) {
        1409 if (!COMPILED) {
        1410 // The full but non-canonicalized URL for later use.
        1411 var originalPath = src;
        1412 // Canonicalize the path, removing any /./ or /../ since Chrome's debugging
        1413 // console doesn't auto-canonicalize XHR loads as it does <script> srcs.
        1414 src = goog.normalizePath_(src);
        1415
        1416 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT ||
        1417 goog.writeScriptTag_;
        1418
        1419 var scriptText = goog.loadFileSync_(src);
        1420
        1421 if (scriptText != null) {
        1422 var execModuleScript = goog.wrapModule_(src, scriptText);
        1423 var isOldIE = goog.IS_OLD_IE_;
        1424 if (isOldIE) {
        1425 goog.dependencies_.deferred[originalPath] = execModuleScript;
        1426 goog.queuedModules_.push(originalPath);
        1427 } else {
        1428 importScript(src, execModuleScript);
        1429 }
        1430 } else {
        1431 throw new Error('load of ' + src + 'failed');
        1432 }
        1433 }
        1434};
        1435
        1436
        1437//==============================================================================
        1438// Language Enhancements
        1439//==============================================================================
        1440
        1441
        1442/**
        1443 * This is a "fixed" version of the typeof operator. It differs from the typeof
        1444 * operator in such a way that null returns 'null' and arrays return 'array'.
        1445 * @param {?} value The value to get the type of.
        1446 * @return {string} The name of the type.
        1447 */
        1448goog.typeOf = function(value) {
        1449 var s = typeof value;
        1450 if (s == 'object') {
        1451 if (value) {
        1452 // Check these first, so we can avoid calling Object.prototype.toString if
        1453 // possible.
        1454 //
        1455 // IE improperly marshals tyepof across execution contexts, but a
        1456 // cross-context object will still return false for "instanceof Object".
        1457 if (value instanceof Array) {
        1458 return 'array';
        1459 } else if (value instanceof Object) {
        1460 return s;
        1461 }
        1462
        1463 // HACK: In order to use an Object prototype method on the arbitrary
        1464 // value, the compiler requires the value be cast to type Object,
        1465 // even though the ECMA spec explicitly allows it.
        1466 var className = Object.prototype.toString.call(
        1467 /** @type {!Object} */ (value));
        1468 // In Firefox 3.6, attempting to access iframe window objects' length
        1469 // property throws an NS_ERROR_FAILURE, so we need to special-case it
        1470 // here.
        1471 if (className == '[object Window]') {
        1472 return 'object';
        1473 }
        1474
        1475 // We cannot always use constructor == Array or instanceof Array because
        1476 // different frames have different Array objects. In IE6, if the iframe
        1477 // where the array was created is destroyed, the array loses its
        1478 // prototype. Then dereferencing val.splice here throws an exception, so
        1479 // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
        1480 // so that will work. In this case, this function will return false and
        1481 // most array functions will still work because the array is still
        1482 // array-like (supports length and []) even though it has lost its
        1483 // prototype.
        1484 // Mark Miller noticed that Object.prototype.toString
        1485 // allows access to the unforgeable [[Class]] property.
        1486 // 15.2.4.2 Object.prototype.toString ( )
        1487 // When the toString method is called, the following steps are taken:
        1488 // 1. Get the [[Class]] property of this object.
        1489 // 2. Compute a string value by concatenating the three strings
        1490 // "[object ", Result(1), and "]".
        1491 // 3. Return Result(2).
        1492 // and this behavior survives the destruction of the execution context.
        1493 if ((className == '[object Array]' ||
        1494 // In IE all non value types are wrapped as objects across window
        1495 // boundaries (not iframe though) so we have to do object detection
        1496 // for this edge case.
        1497 typeof value.length == 'number' &&
        1498 typeof value.splice != 'undefined' &&
        1499 typeof value.propertyIsEnumerable != 'undefined' &&
        1500 !value.propertyIsEnumerable('splice')
        1501
        1502 )) {
        1503 return 'array';
        1504 }
        1505 // HACK: There is still an array case that fails.
        1506 // function ArrayImpostor() {}
        1507 // ArrayImpostor.prototype = [];
        1508 // var impostor = new ArrayImpostor;
        1509 // this can be fixed by getting rid of the fast path
        1510 // (value instanceof Array) and solely relying on
        1511 // (value && Object.prototype.toString.vall(value) === '[object Array]')
        1512 // but that would require many more function calls and is not warranted
        1513 // unless closure code is receiving objects from untrusted sources.
        1514
        1515 // IE in cross-window calls does not correctly marshal the function type
        1516 // (it appears just as an object) so we cannot use just typeof val ==
        1517 // 'function'. However, if the object has a call property, it is a
        1518 // function.
        1519 if ((className == '[object Function]' ||
        1520 typeof value.call != 'undefined' &&
        1521 typeof value.propertyIsEnumerable != 'undefined' &&
        1522 !value.propertyIsEnumerable('call'))) {
        1523 return 'function';
        1524 }
        1525
        1526 } else {
        1527 return 'null';
        1528 }
        1529
        1530 } else if (s == 'function' && typeof value.call == 'undefined') {
        1531 // In Safari typeof nodeList returns 'function', and on Firefox typeof
        1532 // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We
        1533 // would like to return object for those and we can detect an invalid
        1534 // function by making sure that the function object has a call method.
        1535 return 'object';
        1536 }
        1537 return s;
        1538};
        1539
        1540
        1541/**
        1542 * Returns true if the specified value is null.
        1543 * @param {?} val Variable to test.
        1544 * @return {boolean} Whether variable is null.
        1545 */
        1546goog.isNull = function(val) {
        1547 return val === null;
        1548};
        1549
        1550
        1551/**
        1552 * Returns true if the specified value is defined and not null.
        1553 * @param {?} val Variable to test.
        1554 * @return {boolean} Whether variable is defined and not null.
        1555 */
        1556goog.isDefAndNotNull = function(val) {
        1557 // Note that undefined == null.
        1558 return val != null;
        1559};
        1560
        1561
        1562/**
        1563 * Returns true if the specified value is an array.
        1564 * @param {?} val Variable to test.
        1565 * @return {boolean} Whether variable is an array.
        1566 */
        1567goog.isArray = function(val) {
        1568 return goog.typeOf(val) == 'array';
        1569};
        1570
        1571
        1572/**
        1573 * Returns true if the object looks like an array. To qualify as array like
        1574 * the value needs to be either a NodeList or an object with a Number length
        1575 * property. As a special case, a function value is not array like, because its
        1576 * length property is fixed to correspond to the number of expected arguments.
        1577 * @param {?} val Variable to test.
        1578 * @return {boolean} Whether variable is an array.
        1579 */
        1580goog.isArrayLike = function(val) {
        1581 var type = goog.typeOf(val);
        1582 // We do not use goog.isObject here in order to exclude function values.
        1583 return type == 'array' || type == 'object' && typeof val.length == 'number';
        1584};
        1585
        1586
        1587/**
        1588 * Returns true if the object looks like a Date. To qualify as Date-like the
        1589 * value needs to be an object and have a getFullYear() function.
        1590 * @param {?} val Variable to test.
        1591 * @return {boolean} Whether variable is a like a Date.
        1592 */
        1593goog.isDateLike = function(val) {
        1594 return goog.isObject(val) && typeof val.getFullYear == 'function';
        1595};
        1596
        1597
        1598/**
        1599 * Returns true if the specified value is a string.
        1600 * @param {?} val Variable to test.
        1601 * @return {boolean} Whether variable is a string.
        1602 */
        1603goog.isString = function(val) {
        1604 return typeof val == 'string';
        1605};
        1606
        1607
        1608/**
        1609 * Returns true if the specified value is a boolean.
        1610 * @param {?} val Variable to test.
        1611 * @return {boolean} Whether variable is boolean.
        1612 */
        1613goog.isBoolean = function(val) {
        1614 return typeof val == 'boolean';
        1615};
        1616
        1617
        1618/**
        1619 * Returns true if the specified value is a number.
        1620 * @param {?} val Variable to test.
        1621 * @return {boolean} Whether variable is a number.
        1622 */
        1623goog.isNumber = function(val) {
        1624 return typeof val == 'number';
        1625};
        1626
        1627
        1628/**
        1629 * Returns true if the specified value is a function.
        1630 * @param {?} val Variable to test.
        1631 * @return {boolean} Whether variable is a function.
        1632 */
        1633goog.isFunction = function(val) {
        1634 return goog.typeOf(val) == 'function';
        1635};
        1636
        1637
        1638/**
        1639 * Returns true if the specified value is an object. This includes arrays and
        1640 * functions.
        1641 * @param {?} val Variable to test.
        1642 * @return {boolean} Whether variable is an object.
        1643 */
        1644goog.isObject = function(val) {
        1645 var type = typeof val;
        1646 return type == 'object' && val != null || type == 'function';
        1647 // return Object(val) === val also works, but is slower, especially if val is
        1648 // not an object.
        1649};
        1650
        1651
        1652/**
        1653 * Gets a unique ID for an object. This mutates the object so that further calls
        1654 * with the same object as a parameter returns the same value. The unique ID is
        1655 * guaranteed to be unique across the current session amongst objects that are
        1656 * passed into {@code getUid}. There is no guarantee that the ID is unique or
        1657 * consistent across sessions. It is unsafe to generate unique ID for function
        1658 * prototypes.
        1659 *
        1660 * @param {Object} obj The object to get the unique ID for.
        1661 * @return {number} The unique ID for the object.
        1662 */
        1663goog.getUid = function(obj) {
        1664 // TODO(arv): Make the type stricter, do not accept null.
        1665
        1666 // In Opera window.hasOwnProperty exists but always returns false so we avoid
        1667 // using it. As a consequence the unique ID generated for BaseClass.prototype
        1668 // and SubClass.prototype will be the same.
        1669 return obj[goog.UID_PROPERTY_] ||
        1670 (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
        1671};
        1672
        1673
        1674/**
        1675 * Whether the given object is already assigned a unique ID.
        1676 *
        1677 * This does not modify the object.
        1678 *
        1679 * @param {!Object} obj The object to check.
        1680 * @return {boolean} Whether there is an assigned unique id for the object.
        1681 */
        1682goog.hasUid = function(obj) {
        1683 return !!obj[goog.UID_PROPERTY_];
        1684};
        1685
        1686
        1687/**
        1688 * Removes the unique ID from an object. This is useful if the object was
        1689 * previously mutated using {@code goog.getUid} in which case the mutation is
        1690 * undone.
        1691 * @param {Object} obj The object to remove the unique ID field from.
        1692 */
        1693goog.removeUid = function(obj) {
        1694 // TODO(arv): Make the type stricter, do not accept null.
        1695
        1696 // In IE, DOM nodes are not instances of Object and throw an exception if we
        1697 // try to delete. Instead we try to use removeAttribute.
        1698 if ('removeAttribute' in /** @type {!Object} */ (obj)) {
        1699 obj.removeAttribute(goog.UID_PROPERTY_);
        1700 }
        1701 /** @preserveTry */
        1702 try {
        1703 delete obj[goog.UID_PROPERTY_];
        1704 } catch (ex) {
        1705 }
        1706};
        1707
        1708
        1709/**
        1710 * Name for unique ID property. Initialized in a way to help avoid collisions
        1711 * with other closure JavaScript on the same page.
        1712 * @type {string}
        1713 * @private
        1714 */
        1715goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);
        1716
        1717
        1718/**
        1719 * Counter for UID.
        1720 * @type {number}
        1721 * @private
        1722 */
        1723goog.uidCounter_ = 0;
        1724
        1725
        1726/**
        1727 * Adds a hash code field to an object. The hash code is unique for the
        1728 * given object.
        1729 * @param {Object} obj The object to get the hash code for.
        1730 * @return {number} The hash code for the object.
        1731 * @deprecated Use goog.getUid instead.
        1732 */
        1733goog.getHashCode = goog.getUid;
        1734
        1735
        1736/**
        1737 * Removes the hash code field from an object.
        1738 * @param {Object} obj The object to remove the field from.
        1739 * @deprecated Use goog.removeUid instead.
        1740 */
        1741goog.removeHashCode = goog.removeUid;
        1742
        1743
        1744/**
        1745 * Clones a value. The input may be an Object, Array, or basic type. Objects and
        1746 * arrays will be cloned recursively.
        1747 *
        1748 * WARNINGS:
        1749 * <code>goog.cloneObject</code> does not detect reference loops. Objects that
        1750 * refer to themselves will cause infinite recursion.
        1751 *
        1752 * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
        1753 * UIDs created by <code>getUid</code> into cloned results.
        1754 *
        1755 * @param {*} obj The value to clone.
        1756 * @return {*} A clone of the input value.
        1757 * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
        1758 */
        1759goog.cloneObject = function(obj) {
        1760 var type = goog.typeOf(obj);
        1761 if (type == 'object' || type == 'array') {
        1762 if (obj.clone) {
        1763 return obj.clone();
        1764 }
        1765 var clone = type == 'array' ? [] : {};
        1766 for (var key in obj) {
        1767 clone[key] = goog.cloneObject(obj[key]);
        1768 }
        1769 return clone;
        1770 }
        1771
        1772 return obj;
        1773};
        1774
        1775
        1776/**
        1777 * A native implementation of goog.bind.
        1778 * @param {Function} fn A function to partially apply.
        1779 * @param {Object|undefined} selfObj Specifies the object which this should
        1780 * point to when the function is run.
        1781 * @param {...*} var_args Additional arguments that are partially applied to the
        1782 * function.
        1783 * @return {!Function} A partially-applied form of the function bind() was
        1784 * invoked as a method of.
        1785 * @private
        1786 * @suppress {deprecated} The compiler thinks that Function.prototype.bind is
        1787 * deprecated because some people have declared a pure-JS version.
        1788 * Only the pure-JS version is truly deprecated.
        1789 */
        1790goog.bindNative_ = function(fn, selfObj, var_args) {
        1791 return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
        1792};
        1793
        1794
        1795/**
        1796 * A pure-JS implementation of goog.bind.
        1797 * @param {Function} fn A function to partially apply.
        1798 * @param {Object|undefined} selfObj Specifies the object which this should
        1799 * point to when the function is run.
        1800 * @param {...*} var_args Additional arguments that are partially applied to the
        1801 * function.
        1802 * @return {!Function} A partially-applied form of the function bind() was
        1803 * invoked as a method of.
        1804 * @private
        1805 */
        1806goog.bindJs_ = function(fn, selfObj, var_args) {
        1807 if (!fn) {
        1808 throw new Error();
        1809 }
        1810
        1811 if (arguments.length > 2) {
        1812 var boundArgs = Array.prototype.slice.call(arguments, 2);
        1813 return function() {
        1814 // Prepend the bound arguments to the current arguments.
        1815 var newArgs = Array.prototype.slice.call(arguments);
        1816 Array.prototype.unshift.apply(newArgs, boundArgs);
        1817 return fn.apply(selfObj, newArgs);
        1818 };
        1819
        1820 } else {
        1821 return function() {
        1822 return fn.apply(selfObj, arguments);
        1823 };
        1824 }
        1825};
        1826
        1827
        1828/**
        1829 * Partially applies this function to a particular 'this object' and zero or
        1830 * more arguments. The result is a new function with some arguments of the first
        1831 * function pre-filled and the value of this 'pre-specified'.
        1832 *
        1833 * Remaining arguments specified at call-time are appended to the pre-specified
        1834 * ones.
        1835 *
        1836 * Also see: {@link #partial}.
        1837 *
        1838 * Usage:
        1839 * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
        1840 * barMethBound('arg3', 'arg4');</pre>
        1841 *
        1842 * @param {?function(this:T, ...)} fn A function to partially apply.
        1843 * @param {T} selfObj Specifies the object which this should point to when the
        1844 * function is run.
        1845 * @param {...*} var_args Additional arguments that are partially applied to the
        1846 * function.
        1847 * @return {!Function} A partially-applied form of the function goog.bind() was
        1848 * invoked as a method of.
        1849 * @template T
        1850 * @suppress {deprecated} See above.
        1851 */
        1852goog.bind = function(fn, selfObj, var_args) {
        1853 // TODO(nicksantos): narrow the type signature.
        1854 if (Function.prototype.bind &&
        1855 // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
        1856 // extension environment. This means that for Chrome extensions, they get
        1857 // the implementation of Function.prototype.bind that calls goog.bind
        1858 // instead of the native one. Even worse, we don't want to introduce a
        1859 // circular dependency between goog.bind and Function.prototype.bind, so
        1860 // we have to hack this to make sure it works correctly.
        1861 Function.prototype.bind.toString().indexOf('native code') != -1) {
        1862 goog.bind = goog.bindNative_;
        1863 } else {
        1864 goog.bind = goog.bindJs_;
        1865 }
        1866 return goog.bind.apply(null, arguments);
        1867};
        1868
        1869
        1870/**
        1871 * Like goog.bind(), except that a 'this object' is not required. Useful when
        1872 * the target function is already bound.
        1873 *
        1874 * Usage:
        1875 * var g = goog.partial(f, arg1, arg2);
        1876 * g(arg3, arg4);
        1877 *
        1878 * @param {Function} fn A function to partially apply.
        1879 * @param {...*} var_args Additional arguments that are partially applied to fn.
        1880 * @return {!Function} A partially-applied form of the function goog.partial()
        1881 * was invoked as a method of.
        1882 */
        1883goog.partial = function(fn, var_args) {
        1884 var args = Array.prototype.slice.call(arguments, 1);
        1885 return function() {
        1886 // Clone the array (with slice()) and append additional arguments
        1887 // to the existing arguments.
        1888 var newArgs = args.slice();
        1889 newArgs.push.apply(newArgs, arguments);
        1890 return fn.apply(this, newArgs);
        1891 };
        1892};
        1893
        1894
        1895/**
        1896 * Copies all the members of a source object to a target object. This method
        1897 * does not work on all browsers for all objects that contain keys such as
        1898 * toString or hasOwnProperty. Use goog.object.extend for this purpose.
        1899 * @param {Object} target Target.
        1900 * @param {Object} source Source.
        1901 */
        1902goog.mixin = function(target, source) {
        1903 for (var x in source) {
        1904 target[x] = source[x];
        1905 }
        1906
        1907 // For IE7 or lower, the for-in-loop does not contain any properties that are
        1908 // not enumerable on the prototype object (for example, isPrototypeOf from
        1909 // Object.prototype) but also it will not include 'replace' on objects that
        1910 // extend String and change 'replace' (not that it is common for anyone to
        1911 // extend anything except Object).
        1912};
        1913
        1914
        1915/**
        1916 * @return {number} An integer value representing the number of milliseconds
        1917 * between midnight, January 1, 1970 and the current time.
        1918 */
        1919goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
        1920 // Unary plus operator converts its operand to a number which in the case of
        1921 // a date is done by calling getTime().
        1922 return +new Date();
        1923});
        1924
        1925
        1926/**
        1927 * Evals JavaScript in the global scope. In IE this uses execScript, other
        1928 * browsers use goog.global.eval. If goog.global.eval does not evaluate in the
        1929 * global scope (for example, in Safari), appends a script tag instead.
        1930 * Throws an exception if neither execScript or eval is defined.
        1931 * @param {string} script JavaScript string.
        1932 */
        1933goog.globalEval = function(script) {
        1934 if (goog.global.execScript) {
        1935 goog.global.execScript(script, 'JavaScript');
        1936 } else if (goog.global.eval) {
        1937 // Test to see if eval works
        1938 if (goog.evalWorksForGlobals_ == null) {
        1939 goog.global.eval('var _evalTest_ = 1;');
        1940 if (typeof goog.global['_evalTest_'] != 'undefined') {
        1941 try {
        1942 delete goog.global['_evalTest_'];
        1943 } catch (ignore) {
        1944 // Microsoft edge fails the deletion above in strict mode.
        1945 }
        1946 goog.evalWorksForGlobals_ = true;
        1947 } else {
        1948 goog.evalWorksForGlobals_ = false;
        1949 }
        1950 }
        1951
        1952 if (goog.evalWorksForGlobals_) {
        1953 goog.global.eval(script);
        1954 } else {
        1955 /** @type {Document} */
        1956 var doc = goog.global.document;
        1957 var scriptElt = /** @type {!HTMLScriptElement} */ (
        1958 doc.createElement('SCRIPT'));
        1959 scriptElt.type = 'text/javascript';
        1960 scriptElt.defer = false;
        1961 // Note(user): can't use .innerHTML since "t('<test>')" will fail and
        1962 // .text doesn't work in Safari 2. Therefore we append a text node.
        1963 scriptElt.appendChild(doc.createTextNode(script));
        1964 doc.body.appendChild(scriptElt);
        1965 doc.body.removeChild(scriptElt);
        1966 }
        1967 } else {
        1968 throw Error('goog.globalEval not available');
        1969 }
        1970};
        1971
        1972
        1973/**
        1974 * Indicates whether or not we can call 'eval' directly to eval code in the
        1975 * global scope. Set to a Boolean by the first call to goog.globalEval (which
        1976 * empirically tests whether eval works for globals). @see goog.globalEval
        1977 * @type {?boolean}
        1978 * @private
        1979 */
        1980goog.evalWorksForGlobals_ = null;
        1981
        1982
        1983/**
        1984 * Optional map of CSS class names to obfuscated names used with
        1985 * goog.getCssName().
        1986 * @private {!Object<string, string>|undefined}
        1987 * @see goog.setCssNameMapping
        1988 */
        1989goog.cssNameMapping_;
        1990
        1991
        1992/**
        1993 * Optional obfuscation style for CSS class names. Should be set to either
        1994 * 'BY_WHOLE' or 'BY_PART' if defined.
        1995 * @type {string|undefined}
        1996 * @private
        1997 * @see goog.setCssNameMapping
        1998 */
        1999goog.cssNameMappingStyle_;
        2000
        2001
        2002/**
        2003 * Handles strings that are intended to be used as CSS class names.
        2004 *
        2005 * This function works in tandem with @see goog.setCssNameMapping.
        2006 *
        2007 * Without any mapping set, the arguments are simple joined with a hyphen and
        2008 * passed through unaltered.
        2009 *
        2010 * When there is a mapping, there are two possible styles in which these
        2011 * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
        2012 * of the passed in css name is rewritten according to the map. In the BY_WHOLE
        2013 * style, the full css name is looked up in the map directly. If a rewrite is
        2014 * not specified by the map, the compiler will output a warning.
        2015 *
        2016 * When the mapping is passed to the compiler, it will replace calls to
        2017 * goog.getCssName with the strings from the mapping, e.g.
        2018 * var x = goog.getCssName('foo');
        2019 * var y = goog.getCssName(this.baseClass, 'active');
        2020 * becomes:
        2021 * var x = 'foo';
        2022 * var y = this.baseClass + '-active';
        2023 *
        2024 * If one argument is passed it will be processed, if two are passed only the
        2025 * modifier will be processed, as it is assumed the first argument was generated
        2026 * as a result of calling goog.getCssName.
        2027 *
        2028 * @param {string} className The class name.
        2029 * @param {string=} opt_modifier A modifier to be appended to the class name.
        2030 * @return {string} The class name or the concatenation of the class name and
        2031 * the modifier.
        2032 */
        2033goog.getCssName = function(className, opt_modifier) {
        2034 var getMapping = function(cssName) {
        2035 return goog.cssNameMapping_[cssName] || cssName;
        2036 };
        2037
        2038 var renameByParts = function(cssName) {
        2039 // Remap all the parts individually.
        2040 var parts = cssName.split('-');
        2041 var mapped = [];
        2042 for (var i = 0; i < parts.length; i++) {
        2043 mapped.push(getMapping(parts[i]));
        2044 }
        2045 return mapped.join('-');
        2046 };
        2047
        2048 var rename;
        2049 if (goog.cssNameMapping_) {
        2050 rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ?
        2051 getMapping : renameByParts;
        2052 } else {
        2053 rename = function(a) {
        2054 return a;
        2055 };
        2056 }
        2057
        2058 if (opt_modifier) {
        2059 return className + '-' + rename(opt_modifier);
        2060 } else {
        2061 return rename(className);
        2062 }
        2063};
        2064
        2065
        2066/**
        2067 * Sets the map to check when returning a value from goog.getCssName(). Example:
        2068 * <pre>
        2069 * goog.setCssNameMapping({
        2070 * "goog": "a",
        2071 * "disabled": "b",
        2072 * });
        2073 *
        2074 * var x = goog.getCssName('goog');
        2075 * // The following evaluates to: "a a-b".
        2076 * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
        2077 * </pre>
        2078 * When declared as a map of string literals to string literals, the JSCompiler
        2079 * will replace all calls to goog.getCssName() using the supplied map if the
        2080 * --process_closure_primitives flag is set.
        2081 *
        2082 * @param {!Object} mapping A map of strings to strings where keys are possible
        2083 * arguments to goog.getCssName() and values are the corresponding values
        2084 * that should be returned.
        2085 * @param {string=} opt_style The style of css name mapping. There are two valid
        2086 * options: 'BY_PART', and 'BY_WHOLE'.
        2087 * @see goog.getCssName for a description.
        2088 */
        2089goog.setCssNameMapping = function(mapping, opt_style) {
        2090 goog.cssNameMapping_ = mapping;
        2091 goog.cssNameMappingStyle_ = opt_style;
        2092};
        2093
        2094
        2095/**
        2096 * To use CSS renaming in compiled mode, one of the input files should have a
        2097 * call to goog.setCssNameMapping() with an object literal that the JSCompiler
        2098 * can extract and use to replace all calls to goog.getCssName(). In uncompiled
        2099 * mode, JavaScript code should be loaded before this base.js file that declares
        2100 * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
        2101 * to ensure that the mapping is loaded before any calls to goog.getCssName()
        2102 * are made in uncompiled mode.
        2103 *
        2104 * A hook for overriding the CSS name mapping.
        2105 * @type {!Object<string, string>|undefined}
        2106 */
        2107goog.global.CLOSURE_CSS_NAME_MAPPING;
        2108
        2109
        2110if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
        2111 // This does not call goog.setCssNameMapping() because the JSCompiler
        2112 // requires that goog.setCssNameMapping() be called with an object literal.
        2113 goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
        2114}
        2115
        2116
        2117/**
        2118 * Gets a localized message.
        2119 *
        2120 * This function is a compiler primitive. If you give the compiler a localized
        2121 * message bundle, it will replace the string at compile-time with a localized
        2122 * version, and expand goog.getMsg call to a concatenated string.
        2123 *
        2124 * Messages must be initialized in the form:
        2125 * <code>
        2126 * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
        2127 * </code>
        2128 *
        2129 * @param {string} str Translatable string, places holders in the form {$foo}.
        2130 * @param {Object<string, string>=} opt_values Maps place holder name to value.
        2131 * @return {string} message with placeholders filled.
        2132 */
        2133goog.getMsg = function(str, opt_values) {
        2134 if (opt_values) {
        2135 str = str.replace(/\{\$([^}]+)}/g, function(match, key) {
        2136 return (opt_values != null && key in opt_values) ?
        2137 opt_values[key] : match;
        2138 });
        2139 }
        2140 return str;
        2141};
        2142
        2143
        2144/**
        2145 * Gets a localized message. If the message does not have a translation, gives a
        2146 * fallback message.
        2147 *
        2148 * This is useful when introducing a new message that has not yet been
        2149 * translated into all languages.
        2150 *
        2151 * This function is a compiler primitive. Must be used in the form:
        2152 * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
        2153 * where MSG_A and MSG_B were initialized with goog.getMsg.
        2154 *
        2155 * @param {string} a The preferred message.
        2156 * @param {string} b The fallback message.
        2157 * @return {string} The best translated message.
        2158 */
        2159goog.getMsgWithFallback = function(a, b) {
        2160 return a;
        2161};
        2162
        2163
        2164/**
        2165 * Exposes an unobfuscated global namespace path for the given object.
        2166 * Note that fields of the exported object *will* be obfuscated, unless they are
        2167 * exported in turn via this function or goog.exportProperty.
        2168 *
        2169 * Also handy for making public items that are defined in anonymous closures.
        2170 *
        2171 * ex. goog.exportSymbol('public.path.Foo', Foo);
        2172 *
        2173 * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
        2174 * public.path.Foo.staticFunction();
        2175 *
        2176 * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
        2177 * Foo.prototype.myMethod);
        2178 * new public.path.Foo().myMethod();
        2179 *
        2180 * @param {string} publicPath Unobfuscated name to export.
        2181 * @param {*} object Object the name should point to.
        2182 * @param {Object=} opt_objectToExportTo The object to add the path to; default
        2183 * is goog.global.
        2184 */
        2185goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
        2186 goog.exportPath_(publicPath, object, opt_objectToExportTo);
        2187};
        2188
        2189
        2190/**
        2191 * Exports a property unobfuscated into the object's namespace.
        2192 * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
        2193 * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
        2194 * @param {Object} object Object whose static property is being exported.
        2195 * @param {string} publicName Unobfuscated name to export.
        2196 * @param {*} symbol Object the name should point to.
        2197 */
        2198goog.exportProperty = function(object, publicName, symbol) {
        2199 object[publicName] = symbol;
        2200};
        2201
        2202
        2203/**
        2204 * Inherit the prototype methods from one constructor into another.
        2205 *
        2206 * Usage:
        2207 * <pre>
        2208 * function ParentClass(a, b) { }
        2209 * ParentClass.prototype.foo = function(a) { };
        2210 *
        2211 * function ChildClass(a, b, c) {
        2212 * ChildClass.base(this, 'constructor', a, b);
        2213 * }
        2214 * goog.inherits(ChildClass, ParentClass);
        2215 *
        2216 * var child = new ChildClass('a', 'b', 'see');
        2217 * child.foo(); // This works.
        2218 * </pre>
        2219 *
        2220 * @param {!Function} childCtor Child class.
        2221 * @param {!Function} parentCtor Parent class.
        2222 */
        2223goog.inherits = function(childCtor, parentCtor) {
        2224 /** @constructor */
        2225 function tempCtor() {};
        2226 tempCtor.prototype = parentCtor.prototype;
        2227 childCtor.superClass_ = parentCtor.prototype;
        2228 childCtor.prototype = new tempCtor();
        2229 /** @override */
        2230 childCtor.prototype.constructor = childCtor;
        2231
        2232 /**
        2233 * Calls superclass constructor/method.
        2234 *
        2235 * This function is only available if you use goog.inherits to
        2236 * express inheritance relationships between classes.
        2237 *
        2238 * NOTE: This is a replacement for goog.base and for superClass_
        2239 * property defined in childCtor.
        2240 *
        2241 * @param {!Object} me Should always be "this".
        2242 * @param {string} methodName The method name to call. Calling
        2243 * superclass constructor can be done with the special string
        2244 * 'constructor'.
        2245 * @param {...*} var_args The arguments to pass to superclass
        2246 * method/constructor.
        2247 * @return {*} The return value of the superclass method/constructor.
        2248 */
        2249 childCtor.base = function(me, methodName, var_args) {
        2250 // Copying using loop to avoid deop due to passing arguments object to
        2251 // function. This is faster in many JS engines as of late 2014.
        2252 var args = new Array(arguments.length - 2);
        2253 for (var i = 2; i < arguments.length; i++) {
        2254 args[i - 2] = arguments[i];
        2255 }
        2256 return parentCtor.prototype[methodName].apply(me, args);
        2257 };
        2258};
        2259
        2260
        2261/**
        2262 * Call up to the superclass.
        2263 *
        2264 * If this is called from a constructor, then this calls the superclass
        2265 * constructor with arguments 1-N.
        2266 *
        2267 * If this is called from a prototype method, then you must pass the name of the
        2268 * method as the second argument to this function. If you do not, you will get a
        2269 * runtime error. This calls the superclass' method with arguments 2-N.
        2270 *
        2271 * This function only works if you use goog.inherits to express inheritance
        2272 * relationships between your classes.
        2273 *
        2274 * This function is a compiler primitive. At compile-time, the compiler will do
        2275 * macro expansion to remove a lot of the extra overhead that this function
        2276 * introduces. The compiler will also enforce a lot of the assumptions that this
        2277 * function makes, and treat it as a compiler error if you break them.
        2278 *
        2279 * @param {!Object} me Should always be "this".
        2280 * @param {*=} opt_methodName The method name if calling a super method.
        2281 * @param {...*} var_args The rest of the arguments.
        2282 * @return {*} The return value of the superclass method.
        2283 * @suppress {es5Strict} This method can not be used in strict mode, but
        2284 * all Closure Library consumers must depend on this file.
        2285 */
        2286goog.base = function(me, opt_methodName, var_args) {
        2287 var caller = arguments.callee.caller;
        2288
        2289 if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) {
        2290 throw Error('arguments.caller not defined. goog.base() cannot be used ' +
        2291 'with strict mode code. See ' +
        2292 'http://www.ecma-international.org/ecma-262/5.1/#sec-C');
        2293 }
        2294
        2295 if (caller.superClass_) {
        2296 // Copying using loop to avoid deop due to passing arguments object to
        2297 // function. This is faster in many JS engines as of late 2014.
        2298 var ctorArgs = new Array(arguments.length - 1);
        2299 for (var i = 1; i < arguments.length; i++) {
        2300 ctorArgs[i - 1] = arguments[i];
        2301 }
        2302 // This is a constructor. Call the superclass constructor.
        2303 return caller.superClass_.constructor.apply(me, ctorArgs);
        2304 }
        2305
        2306 // Copying using loop to avoid deop due to passing arguments object to
        2307 // function. This is faster in many JS engines as of late 2014.
        2308 var args = new Array(arguments.length - 2);
        2309 for (var i = 2; i < arguments.length; i++) {
        2310 args[i - 2] = arguments[i];
        2311 }
        2312 var foundCaller = false;
        2313 for (var ctor = me.constructor;
        2314 ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
        2315 if (ctor.prototype[opt_methodName] === caller) {
        2316 foundCaller = true;
        2317 } else if (foundCaller) {
        2318 return ctor.prototype[opt_methodName].apply(me, args);
        2319 }
        2320 }
        2321
        2322 // If we did not find the caller in the prototype chain, then one of two
        2323 // things happened:
        2324 // 1) The caller is an instance method.
        2325 // 2) This method was not called by the right caller.
        2326 if (me[opt_methodName] === caller) {
        2327 return me.constructor.prototype[opt_methodName].apply(me, args);
        2328 } else {
        2329 throw Error(
        2330 'goog.base called from a method of one name ' +
        2331 'to a method of a different name');
        2332 }
        2333};
        2334
        2335
        2336/**
        2337 * Allow for aliasing within scope functions. This function exists for
        2338 * uncompiled code - in compiled code the calls will be inlined and the aliases
        2339 * applied. In uncompiled code the function is simply run since the aliases as
        2340 * written are valid JavaScript.
        2341 *
        2342 *
        2343 * @param {function()} fn Function to call. This function can contain aliases
        2344 * to namespaces (e.g. "var dom = goog.dom") or classes
        2345 * (e.g. "var Timer = goog.Timer").
        2346 */
        2347goog.scope = function(fn) {
        2348 fn.call(goog.global);
        2349};
        2350
        2351
        2352/*
        2353 * To support uncompiled, strict mode bundles that use eval to divide source
        2354 * like so:
        2355 * eval('someSource;//# sourceUrl sourcefile.js');
        2356 * We need to export the globally defined symbols "goog" and "COMPILED".
        2357 * Exporting "goog" breaks the compiler optimizations, so we required that
        2358 * be defined externally.
        2359 * NOTE: We don't use goog.exportSymbol here because we don't want to trigger
        2360 * extern generation when that compiler option is enabled.
        2361 */
        2362if (!COMPILED) {
        2363 goog.global['COMPILED'] = COMPILED;
        2364}
        2365
        2366
        2367//==============================================================================
        2368// goog.defineClass implementation
        2369//==============================================================================
        2370
        2371
        2372/**
        2373 * Creates a restricted form of a Closure "class":
        2374 * - from the compiler's perspective, the instance returned from the
        2375 * constructor is sealed (no new properties may be added). This enables
        2376 * better checks.
        2377 * - the compiler will rewrite this definition to a form that is optimal
        2378 * for type checking and optimization (initially this will be a more
        2379 * traditional form).
        2380 *
        2381 * @param {Function} superClass The superclass, Object or null.
        2382 * @param {goog.defineClass.ClassDescriptor} def
        2383 * An object literal describing
        2384 * the class. It may have the following properties:
        2385 * "constructor": the constructor function
        2386 * "statics": an object literal containing methods to add to the constructor
        2387 * as "static" methods or a function that will receive the constructor
        2388 * function as its only parameter to which static properties can
        2389 * be added.
        2390 * all other properties are added to the prototype.
        2391 * @return {!Function} The class constructor.
        2392 */
        2393goog.defineClass = function(superClass, def) {
        2394 // TODO(johnlenz): consider making the superClass an optional parameter.
        2395 var constructor = def.constructor;
        2396 var statics = def.statics;
        2397 // Wrap the constructor prior to setting up the prototype and static methods.
        2398 if (!constructor || constructor == Object.prototype.constructor) {
        2399 constructor = function() {
        2400 throw Error('cannot instantiate an interface (no constructor defined).');
        2401 };
        2402 }
        2403
        2404 var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);
        2405 if (superClass) {
        2406 goog.inherits(cls, superClass);
        2407 }
        2408
        2409 // Remove all the properties that should not be copied to the prototype.
        2410 delete def.constructor;
        2411 delete def.statics;
        2412
        2413 goog.defineClass.applyProperties_(cls.prototype, def);
        2414 if (statics != null) {
        2415 if (statics instanceof Function) {
        2416 statics(cls);
        2417 } else {
        2418 goog.defineClass.applyProperties_(cls, statics);
        2419 }
        2420 }
        2421
        2422 return cls;
        2423};
        2424
        2425
        2426/**
        2427 * @typedef {
        2428 * !Object|
        2429 * {constructor:!Function}|
        2430 * {constructor:!Function, statics:(Object|function(Function):void)}}
        2431 * @suppress {missingProvide}
        2432 */
        2433goog.defineClass.ClassDescriptor;
        2434
        2435
        2436/**
        2437 * @define {boolean} Whether the instances returned by
        2438 * goog.defineClass should be sealed when possible.
        2439 */
        2440goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);
        2441
        2442
        2443/**
        2444 * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is
        2445 * defined, this function will wrap the constructor in a function that seals the
        2446 * results of the provided constructor function.
        2447 *
        2448 * @param {!Function} ctr The constructor whose results maybe be sealed.
        2449 * @param {Function} superClass The superclass constructor.
        2450 * @return {!Function} The replacement constructor.
        2451 * @private
        2452 */
        2453goog.defineClass.createSealingConstructor_ = function(ctr, superClass) {
        2454 if (goog.defineClass.SEAL_CLASS_INSTANCES &&
        2455 Object.seal instanceof Function) {
        2456 // Don't seal subclasses of unsealable-tagged legacy classes.
        2457 if (superClass && superClass.prototype &&
        2458 superClass.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]) {
        2459 return ctr;
        2460 }
        2461 /**
        2462 * @this {Object}
        2463 * @return {?}
        2464 */
        2465 var wrappedCtr = function() {
        2466 // Don't seal an instance of a subclass when it calls the constructor of
        2467 // its super class as there is most likely still setup to do.
        2468 var instance = ctr.apply(this, arguments) || this;
        2469 instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];
        2470 if (this.constructor === wrappedCtr) {
        2471 Object.seal(instance);
        2472 }
        2473 return instance;
        2474 };
        2475 return wrappedCtr;
        2476 }
        2477 return ctr;
        2478};
        2479
        2480
        2481// TODO(johnlenz): share these values with the goog.object
        2482/**
        2483 * The names of the fields that are defined on Object.prototype.
        2484 * @type {!Array<string>}
        2485 * @private
        2486 * @const
        2487 */
        2488goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [
        2489 'constructor',
        2490 'hasOwnProperty',
        2491 'isPrototypeOf',
        2492 'propertyIsEnumerable',
        2493 'toLocaleString',
        2494 'toString',
        2495 'valueOf'
        2496];
        2497
        2498
        2499// TODO(johnlenz): share this function with the goog.object
        2500/**
        2501 * @param {!Object} target The object to add properties to.
        2502 * @param {!Object} source The object to copy properties from.
        2503 * @private
        2504 */
        2505goog.defineClass.applyProperties_ = function(target, source) {
        2506 // TODO(johnlenz): update this to support ES5 getters/setters
        2507
        2508 var key;
        2509 for (key in source) {
        2510 if (Object.prototype.hasOwnProperty.call(source, key)) {
        2511 target[key] = source[key];
        2512 }
        2513 }
        2514
        2515 // For IE the for-in-loop does not contain any properties that are not
        2516 // enumerable on the prototype object (for example isPrototypeOf from
        2517 // Object.prototype) and it will also not include 'replace' on objects that
        2518 // extend String and change 'replace' (not that it is common for anyone to
        2519 // extend anything except Object).
        2520 for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
        2521 key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];
        2522 if (Object.prototype.hasOwnProperty.call(source, key)) {
        2523 target[key] = source[key];
        2524 }
        2525 }
        2526};
        2527
        2528
        2529/**
        2530 * Sealing classes breaks the older idiom of assigning properties on the
        2531 * prototype rather than in the constructor. As such, goog.defineClass
        2532 * must not seal subclasses of these old-style classes until they are fixed.
        2533 * Until then, this marks a class as "broken", instructing defineClass
        2534 * not to seal subclasses.
        2535 * @param {!Function} ctr The legacy constructor to tag as unsealable.
        2536 */
        2537goog.tagUnsealableClass = function(ctr) {
        2538 if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {
        2539 ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;
        2540 }
        2541};
        2542
        2543
        2544/**
        2545 * Name for unsealable tag property.
        2546 * @const @private {string}
        2547 */
        2548goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable';
        \ No newline at end of file diff --git a/docs/source/lib/goog/debug/debug.js.src.html b/docs/source/lib/goog/debug/debug.js.src.html new file mode 100644 index 0000000..11ee87e --- /dev/null +++ b/docs/source/lib/goog/debug/debug.js.src.html @@ -0,0 +1 @@ +debug.js

        lib/goog/debug/debug.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 Logging and debugging utilities.
        17 *
        18 * @see ../demos/debug.html
        19 */
        20
        21goog.provide('goog.debug');
        22
        23goog.require('goog.array');
        24goog.require('goog.html.SafeHtml');
        25goog.require('goog.html.SafeUrl');
        26goog.require('goog.html.uncheckedconversions');
        27goog.require('goog.string.Const');
        28goog.require('goog.structs.Set');
        29goog.require('goog.userAgent');
        30
        31
        32/** @define {boolean} Whether logging should be enabled. */
        33goog.define('goog.debug.LOGGING_ENABLED', goog.DEBUG);
        34
        35
        36/**
        37 * Catches onerror events fired by windows and similar objects.
        38 * @param {function(Object)} logFunc The function to call with the error
        39 * information.
        40 * @param {boolean=} opt_cancel Whether to stop the error from reaching the
        41 * browser.
        42 * @param {Object=} opt_target Object that fires onerror events.
        43 */
        44goog.debug.catchErrors = function(logFunc, opt_cancel, opt_target) {
        45 var target = opt_target || goog.global;
        46 var oldErrorHandler = target.onerror;
        47 var retVal = !!opt_cancel;
        48
        49 // Chrome interprets onerror return value backwards (http://crbug.com/92062)
        50 // until it was fixed in webkit revision r94061 (Webkit 535.3). This
        51 // workaround still needs to be skipped in Safari after the webkit change
        52 // gets pushed out in Safari.
        53 // See https://bugs.webkit.org/show_bug.cgi?id=67119
        54 if (goog.userAgent.WEBKIT &&
        55 !goog.userAgent.isVersionOrHigher('535.3')) {
        56 retVal = !retVal;
        57 }
        58
        59 /**
        60 * New onerror handler for this target. This onerror handler follows the spec
        61 * according to
        62 * http://www.whatwg.org/specs/web-apps/current-work/#runtime-script-errors
        63 * The spec was changed in August 2013 to support receiving column information
        64 * and an error object for all scripts on the same origin or cross origin
        65 * scripts with the proper headers. See
        66 * https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
        67 *
        68 * @param {string} message The error message. For cross-origin errors, this
        69 * will be scrubbed to just "Script error.". For new browsers that have
        70 * updated to follow the latest spec, errors that come from origins that
        71 * have proper cross origin headers will not be scrubbed.
        72 * @param {string} url The URL of the script that caused the error. The URL
        73 * will be scrubbed to "" for cross origin scripts unless the script has
        74 * proper cross origin headers and the browser has updated to the latest
        75 * spec.
        76 * @param {number} line The line number in the script that the error
        77 * occurred on.
        78 * @param {number=} opt_col The optional column number that the error
        79 * occurred on. Only browsers that have updated to the latest spec will
        80 * include this.
        81 * @param {Error=} opt_error The optional actual error object for this
        82 * error that should include the stack. Only browsers that have updated
        83 * to the latest spec will inlude this parameter.
        84 * @return {boolean} Whether to prevent the error from reaching the browser.
        85 */
        86 target.onerror = function(message, url, line, opt_col, opt_error) {
        87 if (oldErrorHandler) {
        88 oldErrorHandler(message, url, line, opt_col, opt_error);
        89 }
        90 logFunc({
        91 message: message,
        92 fileName: url,
        93 line: line,
        94 col: opt_col,
        95 error: opt_error
        96 });
        97 return retVal;
        98 };
        99};
        100
        101
        102/**
        103 * Creates a string representing an object and all its properties.
        104 * @param {Object|null|undefined} obj Object to expose.
        105 * @param {boolean=} opt_showFn Show the functions as well as the properties,
        106 * default is false.
        107 * @return {string} The string representation of {@code obj}.
        108 */
        109goog.debug.expose = function(obj, opt_showFn) {
        110 if (typeof obj == 'undefined') {
        111 return 'undefined';
        112 }
        113 if (obj == null) {
        114 return 'NULL';
        115 }
        116 var str = [];
        117
        118 for (var x in obj) {
        119 if (!opt_showFn && goog.isFunction(obj[x])) {
        120 continue;
        121 }
        122 var s = x + ' = ';
        123 /** @preserveTry */
        124 try {
        125 s += obj[x];
        126 } catch (e) {
        127 s += '*** ' + e + ' ***';
        128 }
        129 str.push(s);
        130 }
        131 return str.join('\n');
        132};
        133
        134
        135/**
        136 * Creates a string representing a given primitive or object, and for an
        137 * object, all its properties and nested objects. WARNING: If an object is
        138 * given, it and all its nested objects will be modified. To detect reference
        139 * cycles, this method identifies objects using goog.getUid() which mutates the
        140 * object.
        141 * @param {*} obj Object to expose.
        142 * @param {boolean=} opt_showFn Also show properties that are functions (by
        143 * default, functions are omitted).
        144 * @return {string} A string representation of {@code obj}.
        145 */
        146goog.debug.deepExpose = function(obj, opt_showFn) {
        147 var str = [];
        148
        149 var helper = function(obj, space, parentSeen) {
        150 var nestspace = space + ' ';
        151 var seen = new goog.structs.Set(parentSeen);
        152
        153 var indentMultiline = function(str) {
        154 return str.replace(/\n/g, '\n' + space);
        155 };
        156
        157 /** @preserveTry */
        158 try {
        159 if (!goog.isDef(obj)) {
        160 str.push('undefined');
        161 } else if (goog.isNull(obj)) {
        162 str.push('NULL');
        163 } else if (goog.isString(obj)) {
        164 str.push('"' + indentMultiline(obj) + '"');
        165 } else if (goog.isFunction(obj)) {
        166 str.push(indentMultiline(String(obj)));
        167 } else if (goog.isObject(obj)) {
        168 if (seen.contains(obj)) {
        169 str.push('*** reference loop detected ***');
        170 } else {
        171 seen.add(obj);
        172 str.push('{');
        173 for (var x in obj) {
        174 if (!opt_showFn && goog.isFunction(obj[x])) {
        175 continue;
        176 }
        177 str.push('\n');
        178 str.push(nestspace);
        179 str.push(x + ' = ');
        180 helper(obj[x], nestspace, seen);
        181 }
        182 str.push('\n' + space + '}');
        183 }
        184 } else {
        185 str.push(obj);
        186 }
        187 } catch (e) {
        188 str.push('*** ' + e + ' ***');
        189 }
        190 };
        191
        192 helper(obj, '', new goog.structs.Set());
        193 return str.join('');
        194};
        195
        196
        197/**
        198 * Recursively outputs a nested array as a string.
        199 * @param {Array<?>} arr The array.
        200 * @return {string} String representing nested array.
        201 */
        202goog.debug.exposeArray = function(arr) {
        203 var str = [];
        204 for (var i = 0; i < arr.length; i++) {
        205 if (goog.isArray(arr[i])) {
        206 str.push(goog.debug.exposeArray(arr[i]));
        207 } else {
        208 str.push(arr[i]);
        209 }
        210 }
        211 return '[ ' + str.join(', ') + ' ]';
        212};
        213
        214
        215/**
        216 * Exposes an exception that has been caught by a try...catch and outputs the
        217 * error as HTML with a stack trace.
        218 * @param {Object} err Error object or string.
        219 * @param {Function=} opt_fn Optional function to start stack trace from.
        220 * @return {string} Details of exception, as HTML.
        221 */
        222goog.debug.exposeException = function(err, opt_fn) {
        223 var html = goog.debug.exposeExceptionAsHtml(err, opt_fn);
        224 return goog.html.SafeHtml.unwrap(html);
        225};
        226
        227
        228/**
        229 * Exposes an exception that has been caught by a try...catch and outputs the
        230 * error with a stack trace.
        231 * @param {Object} err Error object or string.
        232 * @param {Function=} opt_fn Optional function to start stack trace from.
        233 * @return {!goog.html.SafeHtml} Details of exception.
        234 */
        235goog.debug.exposeExceptionAsHtml = function(err, opt_fn) {
        236 /** @preserveTry */
        237 try {
        238 var e = goog.debug.normalizeErrorObject(err);
        239 // Create the error message
        240 var viewSourceUrl = goog.debug.createViewSourceUrl_(e.fileName);
        241 var error = goog.html.SafeHtml.concat(
        242 goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(
        243 'Message: ' + e.message + '\nUrl: '),
        244 goog.html.SafeHtml.create('a',
        245 {href: viewSourceUrl, target: '_new'}, e.fileName),
        246 goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(
        247 '\nLine: ' + e.lineNumber + '\n\nBrowser stack:\n' +
        248 e.stack + '-> ' + '[end]\n\nJS stack traversal:\n' +
        249 goog.debug.getStacktrace(opt_fn) + '-> '));
        250 return error;
        251 } catch (e2) {
        252 return goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces(
        253 'Exception trying to expose exception! You win, we lose. ' + e2);
        254 }
        255};
        256
        257
        258/**
        259 * @param {?string=} opt_fileName
        260 * @return {!goog.html.SafeUrl} SafeUrl with view-source scheme, pointing at
        261 * fileName.
        262 * @private
        263 */
        264goog.debug.createViewSourceUrl_ = function(opt_fileName) {
        265 if (!goog.isDefAndNotNull(opt_fileName)) {
        266 opt_fileName = '';
        267 }
        268 if (!/^https?:\/\//i.test(opt_fileName)) {
        269 return goog.html.SafeUrl.fromConstant(
        270 goog.string.Const.from('sanitizedviewsrc'));
        271 }
        272 var sanitizedFileName = goog.html.SafeUrl.sanitize(opt_fileName);
        273 return goog.html.uncheckedconversions.
        274 safeUrlFromStringKnownToSatisfyTypeContract(
        275 goog.string.Const.from('view-source scheme plus HTTP/HTTPS URL'),
        276 'view-source:' + goog.html.SafeUrl.unwrap(sanitizedFileName));
        277};
        278
        279
        280/**
        281 * Normalizes the error/exception object between browsers.
        282 * @param {Object} err Raw error object.
        283 * @return {!Object} Normalized error object.
        284 */
        285goog.debug.normalizeErrorObject = function(err) {
        286 var href = goog.getObjectByName('window.location.href');
        287 if (goog.isString(err)) {
        288 return {
        289 'message': err,
        290 'name': 'Unknown error',
        291 'lineNumber': 'Not available',
        292 'fileName': href,
        293 'stack': 'Not available'
        294 };
        295 }
        296
        297 var lineNumber, fileName;
        298 var threwError = false;
        299
        300 try {
        301 lineNumber = err.lineNumber || err.line || 'Not available';
        302 } catch (e) {
        303 // Firefox 2 sometimes throws an error when accessing 'lineNumber':
        304 // Message: Permission denied to get property UnnamedClass.lineNumber
        305 lineNumber = 'Not available';
        306 threwError = true;
        307 }
        308
        309 try {
        310 fileName = err.fileName || err.filename || err.sourceURL ||
        311 // $googDebugFname may be set before a call to eval to set the filename
        312 // that the eval is supposed to present.
        313 goog.global['$googDebugFname'] || href;
        314 } catch (e) {
        315 // Firefox 2 may also throw an error when accessing 'filename'.
        316 fileName = 'Not available';
        317 threwError = true;
        318 }
        319
        320 // The IE Error object contains only the name and the message.
        321 // The Safari Error object uses the line and sourceURL fields.
        322 if (threwError || !err.lineNumber || !err.fileName || !err.stack ||
        323 !err.message || !err.name) {
        324 return {
        325 'message': err.message || 'Not available',
        326 'name': err.name || 'UnknownError',
        327 'lineNumber': lineNumber,
        328 'fileName': fileName,
        329 'stack': err.stack || 'Not available'
        330 };
        331 }
        332
        333 // Standards error object
        334 return err;
        335};
        336
        337
        338/**
        339 * Converts an object to an Error if it's a String,
        340 * adds a stacktrace if there isn't one,
        341 * and optionally adds an extra message.
        342 * @param {Error|string} err the original thrown object or string.
        343 * @param {string=} opt_message optional additional message to add to the
        344 * error.
        345 * @return {!Error} If err is a string, it is used to create a new Error,
        346 * which is enhanced and returned. Otherwise err itself is enhanced
        347 * and returned.
        348 */
        349goog.debug.enhanceError = function(err, opt_message) {
        350 var error;
        351 if (typeof err == 'string') {
        352 error = Error(err);
        353 if (Error.captureStackTrace) {
        354 // Trim this function off the call stack, if we can.
        355 Error.captureStackTrace(error, goog.debug.enhanceError);
        356 }
        357 } else {
        358 error = err;
        359 }
        360
        361 if (!error.stack) {
        362 error.stack = goog.debug.getStacktrace(goog.debug.enhanceError);
        363 }
        364 if (opt_message) {
        365 // find the first unoccupied 'messageX' property
        366 var x = 0;
        367 while (error['message' + x]) {
        368 ++x;
        369 }
        370 error['message' + x] = String(opt_message);
        371 }
        372 return error;
        373};
        374
        375
        376/**
        377 * Gets the current stack trace. Simple and iterative - doesn't worry about
        378 * catching circular references or getting the args.
        379 * @param {number=} opt_depth Optional maximum depth to trace back to.
        380 * @return {string} A string with the function names of all functions in the
        381 * stack, separated by \n.
        382 * @suppress {es5Strict}
        383 */
        384goog.debug.getStacktraceSimple = function(opt_depth) {
        385 if (goog.STRICT_MODE_COMPATIBLE) {
        386 var stack = goog.debug.getNativeStackTrace_(goog.debug.getStacktraceSimple);
        387 if (stack) {
        388 return stack;
        389 }
        390 // NOTE: browsers that have strict mode support also have native "stack"
        391 // properties. Fall-through for legacy browser support.
        392 }
        393
        394 var sb = [];
        395 var fn = arguments.callee.caller;
        396 var depth = 0;
        397
        398 while (fn && (!opt_depth || depth < opt_depth)) {
        399 sb.push(goog.debug.getFunctionName(fn));
        400 sb.push('()\n');
        401 /** @preserveTry */
        402 try {
        403 fn = fn.caller;
        404 } catch (e) {
        405 sb.push('[exception trying to get caller]\n');
        406 break;
        407 }
        408 depth++;
        409 if (depth >= goog.debug.MAX_STACK_DEPTH) {
        410 sb.push('[...long stack...]');
        411 break;
        412 }
        413 }
        414 if (opt_depth && depth >= opt_depth) {
        415 sb.push('[...reached max depth limit...]');
        416 } else {
        417 sb.push('[end]');
        418 }
        419
        420 return sb.join('');
        421};
        422
        423
        424/**
        425 * Max length of stack to try and output
        426 * @type {number}
        427 */
        428goog.debug.MAX_STACK_DEPTH = 50;
        429
        430
        431/**
        432 * @param {Function} fn The function to start getting the trace from.
        433 * @return {?string}
        434 * @private
        435 */
        436goog.debug.getNativeStackTrace_ = function(fn) {
        437 var tempErr = new Error();
        438 if (Error.captureStackTrace) {
        439 Error.captureStackTrace(tempErr, fn);
        440 return String(tempErr.stack);
        441 } else {
        442 // IE10, only adds stack traces when an exception is thrown.
        443 try {
        444 throw tempErr;
        445 } catch (e) {
        446 tempErr = e;
        447 }
        448 var stack = tempErr.stack;
        449 if (stack) {
        450 return String(stack);
        451 }
        452 }
        453 return null;
        454};
        455
        456
        457/**
        458 * Gets the current stack trace, either starting from the caller or starting
        459 * from a specified function that's currently on the call stack.
        460 * @param {Function=} opt_fn Optional function to start getting the trace from.
        461 * If not provided, defaults to the function that called this.
        462 * @return {string} Stack trace.
        463 * @suppress {es5Strict}
        464 */
        465goog.debug.getStacktrace = function(opt_fn) {
        466 var stack;
        467 if (goog.STRICT_MODE_COMPATIBLE) {
        468 // Try to get the stack trace from the environment if it is available.
        469 var contextFn = opt_fn || goog.debug.getStacktrace;
        470 stack = goog.debug.getNativeStackTrace_(contextFn);
        471 }
        472 if (!stack) {
        473 // NOTE: browsers that have strict mode support also have native "stack"
        474 // properties. This function will throw in strict mode.
        475 stack = goog.debug.getStacktraceHelper_(
        476 opt_fn || arguments.callee.caller, []);
        477 }
        478 return stack;
        479};
        480
        481
        482/**
        483 * Private helper for getStacktrace().
        484 * @param {Function} fn Function to start getting the trace from.
        485 * @param {Array<!Function>} visited List of functions visited so far.
        486 * @return {string} Stack trace starting from function fn.
        487 * @suppress {es5Strict}
        488 * @private
        489 */
        490goog.debug.getStacktraceHelper_ = function(fn, visited) {
        491 var sb = [];
        492
        493 // Circular reference, certain functions like bind seem to cause a recursive
        494 // loop so we need to catch circular references
        495 if (goog.array.contains(visited, fn)) {
        496 sb.push('[...circular reference...]');
        497
        498 // Traverse the call stack until function not found or max depth is reached
        499 } else if (fn && visited.length < goog.debug.MAX_STACK_DEPTH) {
        500 sb.push(goog.debug.getFunctionName(fn) + '(');
        501 var args = fn.arguments;
        502 // Args may be null for some special functions such as host objects or eval.
        503 for (var i = 0; args && i < args.length; i++) {
        504 if (i > 0) {
        505 sb.push(', ');
        506 }
        507 var argDesc;
        508 var arg = args[i];
        509 switch (typeof arg) {
        510 case 'object':
        511 argDesc = arg ? 'object' : 'null';
        512 break;
        513
        514 case 'string':
        515 argDesc = arg;
        516 break;
        517
        518 case 'number':
        519 argDesc = String(arg);
        520 break;
        521
        522 case 'boolean':
        523 argDesc = arg ? 'true' : 'false';
        524 break;
        525
        526 case 'function':
        527 argDesc = goog.debug.getFunctionName(arg);
        528 argDesc = argDesc ? argDesc : '[fn]';
        529 break;
        530
        531 case 'undefined':
        532 default:
        533 argDesc = typeof arg;
        534 break;
        535 }
        536
        537 if (argDesc.length > 40) {
        538 argDesc = argDesc.substr(0, 40) + '...';
        539 }
        540 sb.push(argDesc);
        541 }
        542 visited.push(fn);
        543 sb.push(')\n');
        544 /** @preserveTry */
        545 try {
        546 sb.push(goog.debug.getStacktraceHelper_(fn.caller, visited));
        547 } catch (e) {
        548 sb.push('[exception trying to get caller]\n');
        549 }
        550
        551 } else if (fn) {
        552 sb.push('[...long stack...]');
        553 } else {
        554 sb.push('[end]');
        555 }
        556 return sb.join('');
        557};
        558
        559
        560/**
        561 * Set a custom function name resolver.
        562 * @param {function(Function): string} resolver Resolves functions to their
        563 * names.
        564 */
        565goog.debug.setFunctionResolver = function(resolver) {
        566 goog.debug.fnNameResolver_ = resolver;
        567};
        568
        569
        570/**
        571 * Gets a function name
        572 * @param {Function} fn Function to get name of.
        573 * @return {string} Function's name.
        574 */
        575goog.debug.getFunctionName = function(fn) {
        576 if (goog.debug.fnNameCache_[fn]) {
        577 return goog.debug.fnNameCache_[fn];
        578 }
        579 if (goog.debug.fnNameResolver_) {
        580 var name = goog.debug.fnNameResolver_(fn);
        581 if (name) {
        582 goog.debug.fnNameCache_[fn] = name;
        583 return name;
        584 }
        585 }
        586
        587 // Heuristically determine function name based on code.
        588 var functionSource = String(fn);
        589 if (!goog.debug.fnNameCache_[functionSource]) {
        590 var matches = /function ([^\(]+)/.exec(functionSource);
        591 if (matches) {
        592 var method = matches[1];
        593 goog.debug.fnNameCache_[functionSource] = method;
        594 } else {
        595 goog.debug.fnNameCache_[functionSource] = '[Anonymous]';
        596 }
        597 }
        598
        599 return goog.debug.fnNameCache_[functionSource];
        600};
        601
        602
        603/**
        604 * Makes whitespace visible by replacing it with printable characters.
        605 * This is useful in finding diffrences between the expected and the actual
        606 * output strings of a testcase.
        607 * @param {string} string whose whitespace needs to be made visible.
        608 * @return {string} string whose whitespace is made visible.
        609 */
        610goog.debug.makeWhitespaceVisible = function(string) {
        611 return string.replace(/ /g, '[_]')
        612 .replace(/\f/g, '[f]')
        613 .replace(/\n/g, '[n]\n')
        614 .replace(/\r/g, '[r]')
        615 .replace(/\t/g, '[t]');
        616};
        617
        618
        619/**
        620 * Returns the type of a value. If a constructor is passed, and a suitable
        621 * string cannot be found, 'unknown type name' will be returned.
        622 *
        623 * <p>Forked rather than moved from {@link goog.asserts.getType_}
        624 * to avoid adding a dependency to goog.asserts.
        625 * @param {*} value A constructor, object, or primitive.
        626 * @return {string} The best display name for the value, or 'unknown type name'.
        627 */
        628goog.debug.runtimeType = function(value) {
        629 if (value instanceof Function) {
        630 return value.displayName || value.name || 'unknown type name';
        631 } else if (value instanceof Object) {
        632 return value.constructor.displayName || value.constructor.name ||
        633 Object.prototype.toString.call(value);
        634 } else {
        635 return value === null ? 'null' : typeof value;
        636 }
        637};
        638
        639
        640/**
        641 * Hash map for storing function names that have already been looked up.
        642 * @type {Object}
        643 * @private
        644 */
        645goog.debug.fnNameCache_ = {};
        646
        647
        648/**
        649 * Resolves functions to their names. Resolved function names will be cached.
        650 * @type {function(Function):string}
        651 * @private
        652 */
        653goog.debug.fnNameResolver_;
        \ No newline at end of file diff --git a/docs/source/lib/goog/debug/entrypointregistry.js.src.html b/docs/source/lib/goog/debug/entrypointregistry.js.src.html new file mode 100644 index 0000000..9bbf8bb --- /dev/null +++ b/docs/source/lib/goog/debug/entrypointregistry.js.src.html @@ -0,0 +1 @@ +entrypointregistry.js

        lib/goog/debug/entrypointregistry.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 A global registry for entry points into a program,
        17 * so that they can be instrumented. Each module should register their
        18 * entry points with this registry. Designed to be compiled out
        19 * if no instrumentation is requested.
        20 *
        21 * Entry points may be registered before or after a call to
        22 * goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
        23 * later, the existing monitor will instrument the new entry point.
        24 *
        25 * @author nicksantos@google.com (Nick Santos)
        26 */
        27
        28goog.provide('goog.debug.EntryPointMonitor');
        29goog.provide('goog.debug.entryPointRegistry');
        30
        31goog.require('goog.asserts');
        32
        33
        34
        35/**
        36 * @interface
        37 */
        38goog.debug.EntryPointMonitor = function() {};
        39
        40
        41/**
        42 * Instruments a function.
        43 *
        44 * @param {!Function} fn A function to instrument.
        45 * @return {!Function} The instrumented function.
        46 */
        47goog.debug.EntryPointMonitor.prototype.wrap;
        48
        49
        50/**
        51 * Try to remove an instrumentation wrapper created by this monitor.
        52 * If the function passed to unwrap is not a wrapper created by this
        53 * monitor, then we will do nothing.
        54 *
        55 * Notice that some wrappers may not be unwrappable. For example, if other
        56 * monitors have applied their own wrappers, then it will be impossible to
        57 * unwrap them because their wrappers will have captured our wrapper.
        58 *
        59 * So it is important that entry points are unwrapped in the reverse
        60 * order that they were wrapped.
        61 *
        62 * @param {!Function} fn A function to unwrap.
        63 * @return {!Function} The unwrapped function, or {@code fn} if it was not
        64 * a wrapped function created by this monitor.
        65 */
        66goog.debug.EntryPointMonitor.prototype.unwrap;
        67
        68
        69/**
        70 * An array of entry point callbacks.
        71 * @type {!Array<function(!Function)>}
        72 * @private
        73 */
        74goog.debug.entryPointRegistry.refList_ = [];
        75
        76
        77/**
        78 * Monitors that should wrap all the entry points.
        79 * @type {!Array<!goog.debug.EntryPointMonitor>}
        80 * @private
        81 */
        82goog.debug.entryPointRegistry.monitors_ = [];
        83
        84
        85/**
        86 * Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
        87 * Checking this allows the compiler to optimize out the registrations.
        88 * @type {boolean}
        89 * @private
        90 */
        91goog.debug.entryPointRegistry.monitorsMayExist_ = false;
        92
        93
        94/**
        95 * Register an entry point with this module.
        96 *
        97 * The entry point will be instrumented when a monitor is passed to
        98 * goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
        99 * entry point is instrumented immediately.
        100 *
        101 * @param {function(!Function)} callback A callback function which is called
        102 * with a transforming function to instrument the entry point. The callback
        103 * is responsible for wrapping the relevant entry point with the
        104 * transforming function.
        105 */
        106goog.debug.entryPointRegistry.register = function(callback) {
        107 // Don't use push(), so that this can be compiled out.
        108 goog.debug.entryPointRegistry.refList_[
        109 goog.debug.entryPointRegistry.refList_.length] = callback;
        110 // If no one calls monitorAll, this can be compiled out.
        111 if (goog.debug.entryPointRegistry.monitorsMayExist_) {
        112 var monitors = goog.debug.entryPointRegistry.monitors_;
        113 for (var i = 0; i < monitors.length; i++) {
        114 callback(goog.bind(monitors[i].wrap, monitors[i]));
        115 }
        116 }
        117};
        118
        119
        120/**
        121 * Configures a monitor to wrap all entry points.
        122 *
        123 * Entry points that have already been registered are immediately wrapped by
        124 * the monitor. When an entry point is registered in the future, it will also
        125 * be wrapped by the monitor when it is registered.
        126 *
        127 * @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
        128 */
        129goog.debug.entryPointRegistry.monitorAll = function(monitor) {
        130 goog.debug.entryPointRegistry.monitorsMayExist_ = true;
        131 var transformer = goog.bind(monitor.wrap, monitor);
        132 for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
        133 goog.debug.entryPointRegistry.refList_[i](transformer);
        134 }
        135 goog.debug.entryPointRegistry.monitors_.push(monitor);
        136};
        137
        138
        139/**
        140 * Try to unmonitor all the entry points that have already been registered. If
        141 * an entry point is registered in the future, it will not be wrapped by the
        142 * monitor when it is registered. Note that this may fail if the entry points
        143 * have additional wrapping.
        144 *
        145 * @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
        146 * the entry points.
        147 * @throws {Error} If the monitor is not the most recently configured monitor.
        148 */
        149goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
        150 var monitors = goog.debug.entryPointRegistry.monitors_;
        151 goog.asserts.assert(monitor == monitors[monitors.length - 1],
        152 'Only the most recent monitor can be unwrapped.');
        153 var transformer = goog.bind(monitor.unwrap, monitor);
        154 for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
        155 goog.debug.entryPointRegistry.refList_[i](transformer);
        156 }
        157 monitors.length--;
        158};
        \ No newline at end of file diff --git a/docs/source/lib/goog/debug/error.js.src.html b/docs/source/lib/goog/debug/error.js.src.html index 7dec952..0b4c27b 100644 --- a/docs/source/lib/goog/debug/error.js.src.html +++ b/docs/source/lib/goog/debug/error.js.src.html @@ -1 +1 @@ -error.js

        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
        24goog.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 */
        34goog.debug.Error = function(opt_msg) {
        35
        36 // Ensure there is a stack trace.
        37 if (Error.captureStackTrace) {
        38 Error.captureStackTrace(this, goog.debug.Error);
        39 } else {
        40 this.stack = new Error().stack || '';
        41 }
        42
        43 if (opt_msg) {
        44 this.message = String(opt_msg);
        45 }
        46};
        47goog.inherits(goog.debug.Error, Error);
        48
        49
        50/** @override */
        51goog.debug.Error.prototype.name = 'CustomError';
        \ No newline at end of file +error.js

        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
        24goog.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 */
        34goog.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 /**
        51 * Whether to report this error to the server. Setting this to false will
        52 * cause the error reporter to not report the error back to the server,
        53 * which can be useful if the client knows that the error has already been
        54 * logged on the server.
        55 * @type {boolean}
        56 */
        57 this.reportErrorToServer = true;
        58};
        59goog.inherits(goog.debug.Error, Error);
        60
        61
        62/** @override */
        63goog.debug.Error.prototype.name = 'CustomError';
        \ No newline at end of file diff --git a/docs/source/lib/goog/debug/logbuffer.js.src.html b/docs/source/lib/goog/debug/logbuffer.js.src.html new file mode 100644 index 0000000..0508996 --- /dev/null +++ b/docs/source/lib/goog/debug/logbuffer.js.src.html @@ -0,0 +1 @@ +logbuffer.js

        lib/goog/debug/logbuffer.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 A buffer for log records. The purpose of this is to improve
        17 * logging performance by re-using old objects when the buffer becomes full and
        18 * to eliminate the need for each app to implement their own log buffer. The
        19 * disadvantage to doing this is that log handlers cannot maintain references to
        20 * log records and expect that they are not overwriten at a later point.
        21 *
        22 * @author agrieve@google.com (Andrew Grieve)
        23 */
        24
        25goog.provide('goog.debug.LogBuffer');
        26
        27goog.require('goog.asserts');
        28goog.require('goog.debug.LogRecord');
        29
        30
        31
        32/**
        33 * Creates the log buffer.
        34 * @constructor
        35 * @final
        36 */
        37goog.debug.LogBuffer = function() {
        38 goog.asserts.assert(goog.debug.LogBuffer.isBufferingEnabled(),
        39 'Cannot use goog.debug.LogBuffer without defining ' +
        40 'goog.debug.LogBuffer.CAPACITY.');
        41 this.clear();
        42};
        43
        44
        45/**
        46 * A static method that always returns the same instance of LogBuffer.
        47 * @return {!goog.debug.LogBuffer} The LogBuffer singleton instance.
        48 */
        49goog.debug.LogBuffer.getInstance = function() {
        50 if (!goog.debug.LogBuffer.instance_) {
        51 // This function is written with the return statement after the assignment
        52 // to avoid the jscompiler StripCode bug described in http://b/2608064.
        53 // After that bug is fixed this can be refactored.
        54 goog.debug.LogBuffer.instance_ = new goog.debug.LogBuffer();
        55 }
        56 return goog.debug.LogBuffer.instance_;
        57};
        58
        59
        60/**
        61 * @define {number} The number of log records to buffer. 0 means disable
        62 * buffering.
        63 */
        64goog.define('goog.debug.LogBuffer.CAPACITY', 0);
        65
        66
        67/**
        68 * The array to store the records.
        69 * @type {!Array<!goog.debug.LogRecord|undefined>}
        70 * @private
        71 */
        72goog.debug.LogBuffer.prototype.buffer_;
        73
        74
        75/**
        76 * The index of the most recently added record or -1 if there are no records.
        77 * @type {number}
        78 * @private
        79 */
        80goog.debug.LogBuffer.prototype.curIndex_;
        81
        82
        83/**
        84 * Whether the buffer is at capacity.
        85 * @type {boolean}
        86 * @private
        87 */
        88goog.debug.LogBuffer.prototype.isFull_;
        89
        90
        91/**
        92 * Adds a log record to the buffer, possibly overwriting the oldest record.
        93 * @param {goog.debug.Logger.Level} level One of the level identifiers.
        94 * @param {string} msg The string message.
        95 * @param {string} loggerName The name of the source logger.
        96 * @return {!goog.debug.LogRecord} The log record.
        97 */
        98goog.debug.LogBuffer.prototype.addRecord = function(level, msg, loggerName) {
        99 var curIndex = (this.curIndex_ + 1) % goog.debug.LogBuffer.CAPACITY;
        100 this.curIndex_ = curIndex;
        101 if (this.isFull_) {
        102 var ret = this.buffer_[curIndex];
        103 ret.reset(level, msg, loggerName);
        104 return ret;
        105 }
        106 this.isFull_ = curIndex == goog.debug.LogBuffer.CAPACITY - 1;
        107 return this.buffer_[curIndex] =
        108 new goog.debug.LogRecord(level, msg, loggerName);
        109};
        110
        111
        112/**
        113 * @return {boolean} Whether the log buffer is enabled.
        114 */
        115goog.debug.LogBuffer.isBufferingEnabled = function() {
        116 return goog.debug.LogBuffer.CAPACITY > 0;
        117};
        118
        119
        120/**
        121 * Removes all buffered log records.
        122 */
        123goog.debug.LogBuffer.prototype.clear = function() {
        124 this.buffer_ = new Array(goog.debug.LogBuffer.CAPACITY);
        125 this.curIndex_ = -1;
        126 this.isFull_ = false;
        127};
        128
        129
        130/**
        131 * Calls the given function for each buffered log record, starting with the
        132 * oldest one.
        133 * @param {function(!goog.debug.LogRecord)} func The function to call.
        134 */
        135goog.debug.LogBuffer.prototype.forEachRecord = function(func) {
        136 var buffer = this.buffer_;
        137 // Corner case: no records.
        138 if (!buffer[0]) {
        139 return;
        140 }
        141 var curIndex = this.curIndex_;
        142 var i = this.isFull_ ? curIndex : -1;
        143 do {
        144 i = (i + 1) % goog.debug.LogBuffer.CAPACITY;
        145 func(/** @type {!goog.debug.LogRecord} */ (buffer[i]));
        146 } while (i != curIndex);
        147};
        148
        \ No newline at end of file diff --git a/docs/source/lib/goog/debug/logger.js.src.html b/docs/source/lib/goog/debug/logger.js.src.html new file mode 100644 index 0000000..031db67 --- /dev/null +++ b/docs/source/lib/goog/debug/logger.js.src.html @@ -0,0 +1 @@ +logger.js

        lib/goog/debug/logger.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 the Logger class. Please minimize dependencies
        17 * this file has on other closure classes as any dependency it takes won't be
        18 * able to use the logging infrastructure.
        19 *
        20 * @see ../demos/debug.html
        21 */
        22
        23goog.provide('goog.debug.LogManager');
        24goog.provide('goog.debug.Loggable');
        25goog.provide('goog.debug.Logger');
        26goog.provide('goog.debug.Logger.Level');
        27
        28goog.require('goog.array');
        29goog.require('goog.asserts');
        30goog.require('goog.debug');
        31goog.require('goog.debug.LogBuffer');
        32goog.require('goog.debug.LogRecord');
        33
        34
        35/**
        36 * A message value that can be handled by a Logger.
        37 *
        38 * Functions are treated like callbacks, but are only called when the event's
        39 * log level is enabled. This is useful for logging messages that are expensive
        40 * to construct.
        41 *
        42 * @typedef {string|function(): string}
        43 */
        44goog.debug.Loggable;
        45
        46
        47
        48/**
        49 * The Logger is an object used for logging debug messages. Loggers are
        50 * normally named, using a hierarchical dot-separated namespace. Logger names
        51 * can be arbitrary strings, but they should normally be based on the package
        52 * name or class name of the logged component, such as goog.net.BrowserChannel.
        53 *
        54 * The Logger object is loosely based on the java class
        55 * java.util.logging.Logger. It supports different levels of filtering for
        56 * different loggers.
        57 *
        58 * The logger object should never be instantiated by application code. It
        59 * should always use the goog.debug.Logger.getLogger function.
        60 *
        61 * @constructor
        62 * @param {string} name The name of the Logger.
        63 * @final
        64 */
        65goog.debug.Logger = function(name) {
        66 /**
        67 * Name of the Logger. Generally a dot-separated namespace
        68 * @private {string}
        69 */
        70 this.name_ = name;
        71
        72 /**
        73 * Parent Logger.
        74 * @private {goog.debug.Logger}
        75 */
        76 this.parent_ = null;
        77
        78 /**
        79 * Level that this logger only filters above. Null indicates it should
        80 * inherit from the parent.
        81 * @private {goog.debug.Logger.Level}
        82 */
        83 this.level_ = null;
        84
        85 /**
        86 * Map of children loggers. The keys are the leaf names of the children and
        87 * the values are the child loggers.
        88 * @private {Object}
        89 */
        90 this.children_ = null;
        91
        92 /**
        93 * Handlers that are listening to this logger.
        94 * @private {Array<Function>}
        95 */
        96 this.handlers_ = null;
        97};
        98
        99
        100/** @const */
        101goog.debug.Logger.ROOT_LOGGER_NAME = '';
        102
        103
        104/**
        105 * @define {boolean} Toggles whether loggers other than the root logger can have
        106 * log handlers attached to them and whether they can have their log level
        107 * set. Logging is a bit faster when this is set to false.
        108 */
        109goog.define('goog.debug.Logger.ENABLE_HIERARCHY', true);
        110
        111
        112if (!goog.debug.Logger.ENABLE_HIERARCHY) {
        113 /**
        114 * @type {!Array<Function>}
        115 * @private
        116 */
        117 goog.debug.Logger.rootHandlers_ = [];
        118
        119
        120 /**
        121 * @type {goog.debug.Logger.Level}
        122 * @private
        123 */
        124 goog.debug.Logger.rootLevel_;
        125}
        126
        127
        128
        129/**
        130 * The Level class defines a set of standard logging levels that
        131 * can be used to control logging output. The logging Level objects
        132 * are ordered and are specified by ordered integers. Enabling logging
        133 * at a given level also enables logging at all higher levels.
        134 * <p>
        135 * Clients should normally use the predefined Level constants such
        136 * as Level.SEVERE.
        137 * <p>
        138 * The levels in descending order are:
        139 * <ul>
        140 * <li>SEVERE (highest value)
        141 * <li>WARNING
        142 * <li>INFO
        143 * <li>CONFIG
        144 * <li>FINE
        145 * <li>FINER
        146 * <li>FINEST (lowest value)
        147 * </ul>
        148 * In addition there is a level OFF that can be used to turn
        149 * off logging, and a level ALL that can be used to enable
        150 * logging of all messages.
        151 *
        152 * @param {string} name The name of the level.
        153 * @param {number} value The numeric value of the level.
        154 * @constructor
        155 * @final
        156 */
        157goog.debug.Logger.Level = function(name, value) {
        158 /**
        159 * The name of the level
        160 * @type {string}
        161 */
        162 this.name = name;
        163
        164 /**
        165 * The numeric value of the level
        166 * @type {number}
        167 */
        168 this.value = value;
        169};
        170
        171
        172/**
        173 * @return {string} String representation of the logger level.
        174 * @override
        175 */
        176goog.debug.Logger.Level.prototype.toString = function() {
        177 return this.name;
        178};
        179
        180
        181/**
        182 * OFF is a special level that can be used to turn off logging.
        183 * This level is initialized to <CODE>Infinity</CODE>.
        184 * @type {!goog.debug.Logger.Level}
        185 */
        186goog.debug.Logger.Level.OFF =
        187 new goog.debug.Logger.Level('OFF', Infinity);
        188
        189
        190/**
        191 * SHOUT is a message level for extra debugging loudness.
        192 * This level is initialized to <CODE>1200</CODE>.
        193 * @type {!goog.debug.Logger.Level}
        194 */
        195goog.debug.Logger.Level.SHOUT = new goog.debug.Logger.Level('SHOUT', 1200);
        196
        197
        198/**
        199 * SEVERE is a message level indicating a serious failure.
        200 * This level is initialized to <CODE>1000</CODE>.
        201 * @type {!goog.debug.Logger.Level}
        202 */
        203goog.debug.Logger.Level.SEVERE = new goog.debug.Logger.Level('SEVERE', 1000);
        204
        205
        206/**
        207 * WARNING is a message level indicating a potential problem.
        208 * This level is initialized to <CODE>900</CODE>.
        209 * @type {!goog.debug.Logger.Level}
        210 */
        211goog.debug.Logger.Level.WARNING = new goog.debug.Logger.Level('WARNING', 900);
        212
        213
        214/**
        215 * INFO is a message level for informational messages.
        216 * This level is initialized to <CODE>800</CODE>.
        217 * @type {!goog.debug.Logger.Level}
        218 */
        219goog.debug.Logger.Level.INFO = new goog.debug.Logger.Level('INFO', 800);
        220
        221
        222/**
        223 * CONFIG is a message level for static configuration messages.
        224 * This level is initialized to <CODE>700</CODE>.
        225 * @type {!goog.debug.Logger.Level}
        226 */
        227goog.debug.Logger.Level.CONFIG = new goog.debug.Logger.Level('CONFIG', 700);
        228
        229
        230/**
        231 * FINE is a message level providing tracing information.
        232 * This level is initialized to <CODE>500</CODE>.
        233 * @type {!goog.debug.Logger.Level}
        234 */
        235goog.debug.Logger.Level.FINE = new goog.debug.Logger.Level('FINE', 500);
        236
        237
        238/**
        239 * FINER indicates a fairly detailed tracing message.
        240 * This level is initialized to <CODE>400</CODE>.
        241 * @type {!goog.debug.Logger.Level}
        242 */
        243goog.debug.Logger.Level.FINER = new goog.debug.Logger.Level('FINER', 400);
        244
        245/**
        246 * FINEST indicates a highly detailed tracing message.
        247 * This level is initialized to <CODE>300</CODE>.
        248 * @type {!goog.debug.Logger.Level}
        249 */
        250
        251goog.debug.Logger.Level.FINEST = new goog.debug.Logger.Level('FINEST', 300);
        252
        253
        254/**
        255 * ALL indicates that all messages should be logged.
        256 * This level is initialized to <CODE>0</CODE>.
        257 * @type {!goog.debug.Logger.Level}
        258 */
        259goog.debug.Logger.Level.ALL = new goog.debug.Logger.Level('ALL', 0);
        260
        261
        262/**
        263 * The predefined levels.
        264 * @type {!Array<!goog.debug.Logger.Level>}
        265 * @final
        266 */
        267goog.debug.Logger.Level.PREDEFINED_LEVELS = [
        268 goog.debug.Logger.Level.OFF,
        269 goog.debug.Logger.Level.SHOUT,
        270 goog.debug.Logger.Level.SEVERE,
        271 goog.debug.Logger.Level.WARNING,
        272 goog.debug.Logger.Level.INFO,
        273 goog.debug.Logger.Level.CONFIG,
        274 goog.debug.Logger.Level.FINE,
        275 goog.debug.Logger.Level.FINER,
        276 goog.debug.Logger.Level.FINEST,
        277 goog.debug.Logger.Level.ALL];
        278
        279
        280/**
        281 * A lookup map used to find the level object based on the name or value of
        282 * the level object.
        283 * @type {Object}
        284 * @private
        285 */
        286goog.debug.Logger.Level.predefinedLevelsCache_ = null;
        287
        288
        289/**
        290 * Creates the predefined levels cache and populates it.
        291 * @private
        292 */
        293goog.debug.Logger.Level.createPredefinedLevelsCache_ = function() {
        294 goog.debug.Logger.Level.predefinedLevelsCache_ = {};
        295 for (var i = 0, level; level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];
        296 i++) {
        297 goog.debug.Logger.Level.predefinedLevelsCache_[level.value] = level;
        298 goog.debug.Logger.Level.predefinedLevelsCache_[level.name] = level;
        299 }
        300};
        301
        302
        303/**
        304 * Gets the predefined level with the given name.
        305 * @param {string} name The name of the level.
        306 * @return {goog.debug.Logger.Level} The level, or null if none found.
        307 */
        308goog.debug.Logger.Level.getPredefinedLevel = function(name) {
        309 if (!goog.debug.Logger.Level.predefinedLevelsCache_) {
        310 goog.debug.Logger.Level.createPredefinedLevelsCache_();
        311 }
        312
        313 return goog.debug.Logger.Level.predefinedLevelsCache_[name] || null;
        314};
        315
        316
        317/**
        318 * Gets the highest predefined level <= #value.
        319 * @param {number} value Level value.
        320 * @return {goog.debug.Logger.Level} The level, or null if none found.
        321 */
        322goog.debug.Logger.Level.getPredefinedLevelByValue = function(value) {
        323 if (!goog.debug.Logger.Level.predefinedLevelsCache_) {
        324 goog.debug.Logger.Level.createPredefinedLevelsCache_();
        325 }
        326
        327 if (value in goog.debug.Logger.Level.predefinedLevelsCache_) {
        328 return goog.debug.Logger.Level.predefinedLevelsCache_[value];
        329 }
        330
        331 for (var i = 0; i < goog.debug.Logger.Level.PREDEFINED_LEVELS.length; ++i) {
        332 var level = goog.debug.Logger.Level.PREDEFINED_LEVELS[i];
        333 if (level.value <= value) {
        334 return level;
        335 }
        336 }
        337 return null;
        338};
        339
        340
        341/**
        342 * Finds or creates a logger for a named subsystem. If a logger has already been
        343 * created with the given name it is returned. Otherwise a new logger is
        344 * created. If a new logger is created its log level will be configured based
        345 * on the LogManager configuration and it will configured to also send logging
        346 * output to its parent's handlers. It will be registered in the LogManager
        347 * global namespace.
        348 *
        349 * @param {string} name A name for the logger. This should be a dot-separated
        350 * name and should normally be based on the package name or class name of the
        351 * subsystem, such as goog.net.BrowserChannel.
        352 * @return {!goog.debug.Logger} The named logger.
        353 * @deprecated use goog.log instead. http://go/goog-debug-logger-deprecated
        354 */
        355goog.debug.Logger.getLogger = function(name) {
        356 return goog.debug.LogManager.getLogger(name);
        357};
        358
        359
        360/**
        361 * Logs a message to profiling tools, if available.
        362 * {@see https://developers.google.com/web-toolkit/speedtracer/logging-api}
        363 * {@see http://msdn.microsoft.com/en-us/library/dd433074(VS.85).aspx}
        364 * @param {string} msg The message to log.
        365 */
        366goog.debug.Logger.logToProfilers = function(msg) {
        367 // Using goog.global, as loggers might be used in window-less contexts.
        368 if (goog.global['console']) {
        369 if (goog.global['console']['timeStamp']) {
        370 // Logs a message to Firebug, Web Inspector, SpeedTracer, etc.
        371 goog.global['console']['timeStamp'](msg);
        372 } else if (goog.global['console']['markTimeline']) {
        373 // TODO(user): markTimeline is deprecated. Drop this else clause entirely
        374 // after Chrome M14 hits stable.
        375 goog.global['console']['markTimeline'](msg);
        376 }
        377 }
        378
        379 if (goog.global['msWriteProfilerMark']) {
        380 // Logs a message to the Microsoft profiler
        381 goog.global['msWriteProfilerMark'](msg);
        382 }
        383};
        384
        385
        386/**
        387 * Gets the name of this logger.
        388 * @return {string} The name of this logger.
        389 */
        390goog.debug.Logger.prototype.getName = function() {
        391 return this.name_;
        392};
        393
        394
        395/**
        396 * Adds a handler to the logger. This doesn't use the event system because
        397 * we want to be able to add logging to the event system.
        398 * @param {Function} handler Handler function to add.
        399 */
        400goog.debug.Logger.prototype.addHandler = function(handler) {
        401 if (goog.debug.LOGGING_ENABLED) {
        402 if (goog.debug.Logger.ENABLE_HIERARCHY) {
        403 if (!this.handlers_) {
        404 this.handlers_ = [];
        405 }
        406 this.handlers_.push(handler);
        407 } else {
        408 goog.asserts.assert(!this.name_,
        409 'Cannot call addHandler on a non-root logger when ' +
        410 'goog.debug.Logger.ENABLE_HIERARCHY is false.');
        411 goog.debug.Logger.rootHandlers_.push(handler);
        412 }
        413 }
        414};
        415
        416
        417/**
        418 * Removes a handler from the logger. This doesn't use the event system because
        419 * we want to be able to add logging to the event system.
        420 * @param {Function} handler Handler function to remove.
        421 * @return {boolean} Whether the handler was removed.
        422 */
        423goog.debug.Logger.prototype.removeHandler = function(handler) {
        424 if (goog.debug.LOGGING_ENABLED) {
        425 var handlers = goog.debug.Logger.ENABLE_HIERARCHY ? this.handlers_ :
        426 goog.debug.Logger.rootHandlers_;
        427 return !!handlers && goog.array.remove(handlers, handler);
        428 } else {
        429 return false;
        430 }
        431};
        432
        433
        434/**
        435 * Returns the parent of this logger.
        436 * @return {goog.debug.Logger} The parent logger or null if this is the root.
        437 */
        438goog.debug.Logger.prototype.getParent = function() {
        439 return this.parent_;
        440};
        441
        442
        443/**
        444 * Returns the children of this logger as a map of the child name to the logger.
        445 * @return {!Object} The map where the keys are the child leaf names and the
        446 * values are the Logger objects.
        447 */
        448goog.debug.Logger.prototype.getChildren = function() {
        449 if (!this.children_) {
        450 this.children_ = {};
        451 }
        452 return this.children_;
        453};
        454
        455
        456/**
        457 * Set the log level specifying which message levels will be logged by this
        458 * logger. Message levels lower than this value will be discarded.
        459 * The level value Level.OFF can be used to turn off logging. If the new level
        460 * is null, it means that this node should inherit its level from its nearest
        461 * ancestor with a specific (non-null) level value.
        462 *
        463 * @param {goog.debug.Logger.Level} level The new level.
        464 */
        465goog.debug.Logger.prototype.setLevel = function(level) {
        466 if (goog.debug.LOGGING_ENABLED) {
        467 if (goog.debug.Logger.ENABLE_HIERARCHY) {
        468 this.level_ = level;
        469 } else {
        470 goog.asserts.assert(!this.name_,
        471 'Cannot call setLevel() on a non-root logger when ' +
        472 'goog.debug.Logger.ENABLE_HIERARCHY is false.');
        473 goog.debug.Logger.rootLevel_ = level;
        474 }
        475 }
        476};
        477
        478
        479/**
        480 * Gets the log level specifying which message levels will be logged by this
        481 * logger. Message levels lower than this value will be discarded.
        482 * The level value Level.OFF can be used to turn off logging. If the level
        483 * is null, it means that this node should inherit its level from its nearest
        484 * ancestor with a specific (non-null) level value.
        485 *
        486 * @return {goog.debug.Logger.Level} The level.
        487 */
        488goog.debug.Logger.prototype.getLevel = function() {
        489 return goog.debug.LOGGING_ENABLED ?
        490 this.level_ : goog.debug.Logger.Level.OFF;
        491};
        492
        493
        494/**
        495 * Returns the effective level of the logger based on its ancestors' levels.
        496 * @return {goog.debug.Logger.Level} The level.
        497 */
        498goog.debug.Logger.prototype.getEffectiveLevel = function() {
        499 if (!goog.debug.LOGGING_ENABLED) {
        500 return goog.debug.Logger.Level.OFF;
        501 }
        502
        503 if (!goog.debug.Logger.ENABLE_HIERARCHY) {
        504 return goog.debug.Logger.rootLevel_;
        505 }
        506 if (this.level_) {
        507 return this.level_;
        508 }
        509 if (this.parent_) {
        510 return this.parent_.getEffectiveLevel();
        511 }
        512 goog.asserts.fail('Root logger has no level set.');
        513 return null;
        514};
        515
        516
        517/**
        518 * Checks if a message of the given level would actually be logged by this
        519 * logger. This check is based on the Loggers effective level, which may be
        520 * inherited from its parent.
        521 * @param {goog.debug.Logger.Level} level The level to check.
        522 * @return {boolean} Whether the message would be logged.
        523 */
        524goog.debug.Logger.prototype.isLoggable = function(level) {
        525 return goog.debug.LOGGING_ENABLED &&
        526 level.value >= this.getEffectiveLevel().value;
        527};
        528
        529
        530/**
        531 * Logs a message. If the logger is currently enabled for the
        532 * given message level then the given message is forwarded to all the
        533 * registered output Handler objects.
        534 * @param {goog.debug.Logger.Level} level One of the level identifiers.
        535 * @param {goog.debug.Loggable} msg The message to log.
        536 * @param {Error|Object=} opt_exception An exception associated with the
        537 * message.
        538 */
        539goog.debug.Logger.prototype.log = function(level, msg, opt_exception) {
        540 // java caches the effective level, not sure it's necessary here
        541 if (goog.debug.LOGGING_ENABLED && this.isLoggable(level)) {
        542 // Message callbacks can be useful when a log message is expensive to build.
        543 if (goog.isFunction(msg)) {
        544 msg = msg();
        545 }
        546
        547 this.doLogRecord_(this.getLogRecord(level, msg, opt_exception));
        548 }
        549};
        550
        551
        552/**
        553 * Creates a new log record and adds the exception (if present) to it.
        554 * @param {goog.debug.Logger.Level} level One of the level identifiers.
        555 * @param {string} msg The string message.
        556 * @param {Error|Object=} opt_exception An exception associated with the
        557 * message.
        558 * @return {!goog.debug.LogRecord} A log record.
        559 * @suppress {es5Strict}
        560 */
        561goog.debug.Logger.prototype.getLogRecord = function(
        562 level, msg, opt_exception) {
        563 if (goog.debug.LogBuffer.isBufferingEnabled()) {
        564 var logRecord =
        565 goog.debug.LogBuffer.getInstance().addRecord(level, msg, this.name_);
        566 } else {
        567 logRecord = new goog.debug.LogRecord(level, String(msg), this.name_);
        568 }
        569 if (opt_exception) {
        570 logRecord.setException(opt_exception);
        571 }
        572 return logRecord;
        573};
        574
        575
        576/**
        577 * Logs a message at the Logger.Level.SHOUT level.
        578 * If the logger is currently enabled for the given message level then the
        579 * given message is forwarded to all the registered output Handler objects.
        580 * @param {goog.debug.Loggable} msg The message to log.
        581 * @param {Error=} opt_exception An exception associated with the message.
        582 */
        583goog.debug.Logger.prototype.shout = function(msg, opt_exception) {
        584 if (goog.debug.LOGGING_ENABLED) {
        585 this.log(goog.debug.Logger.Level.SHOUT, msg, opt_exception);
        586 }
        587};
        588
        589
        590/**
        591 * Logs a message at the Logger.Level.SEVERE level.
        592 * If the logger is currently enabled for the given message level then the
        593 * given message is forwarded to all the registered output Handler objects.
        594 * @param {goog.debug.Loggable} msg The message to log.
        595 * @param {Error=} opt_exception An exception associated with the message.
        596 */
        597goog.debug.Logger.prototype.severe = function(msg, opt_exception) {
        598 if (goog.debug.LOGGING_ENABLED) {
        599 this.log(goog.debug.Logger.Level.SEVERE, msg, opt_exception);
        600 }
        601};
        602
        603
        604/**
        605 * Logs a message at the Logger.Level.WARNING level.
        606 * If the logger is currently enabled for the given message level then the
        607 * given message is forwarded to all the registered output Handler objects.
        608 * @param {goog.debug.Loggable} msg The message to log.
        609 * @param {Error=} opt_exception An exception associated with the message.
        610 */
        611goog.debug.Logger.prototype.warning = function(msg, opt_exception) {
        612 if (goog.debug.LOGGING_ENABLED) {
        613 this.log(goog.debug.Logger.Level.WARNING, msg, opt_exception);
        614 }
        615};
        616
        617
        618/**
        619 * Logs a message at the Logger.Level.INFO level.
        620 * If the logger is currently enabled for the given message level then the
        621 * given message is forwarded to all the registered output Handler objects.
        622 * @param {goog.debug.Loggable} msg The message to log.
        623 * @param {Error=} opt_exception An exception associated with the message.
        624 */
        625goog.debug.Logger.prototype.info = function(msg, opt_exception) {
        626 if (goog.debug.LOGGING_ENABLED) {
        627 this.log(goog.debug.Logger.Level.INFO, msg, opt_exception);
        628 }
        629};
        630
        631
        632/**
        633 * Logs a message at the Logger.Level.CONFIG level.
        634 * If the logger is currently enabled for the given message level then the
        635 * given message is forwarded to all the registered output Handler objects.
        636 * @param {goog.debug.Loggable} msg The message to log.
        637 * @param {Error=} opt_exception An exception associated with the message.
        638 */
        639goog.debug.Logger.prototype.config = function(msg, opt_exception) {
        640 if (goog.debug.LOGGING_ENABLED) {
        641 this.log(goog.debug.Logger.Level.CONFIG, msg, opt_exception);
        642 }
        643};
        644
        645
        646/**
        647 * Logs a message at the Logger.Level.FINE level.
        648 * If the logger is currently enabled for the given message level then the
        649 * given message is forwarded to all the registered output Handler objects.
        650 * @param {goog.debug.Loggable} msg The message to log.
        651 * @param {Error=} opt_exception An exception associated with the message.
        652 */
        653goog.debug.Logger.prototype.fine = function(msg, opt_exception) {
        654 if (goog.debug.LOGGING_ENABLED) {
        655 this.log(goog.debug.Logger.Level.FINE, msg, opt_exception);
        656 }
        657};
        658
        659
        660/**
        661 * Logs a message at the Logger.Level.FINER level.
        662 * If the logger is currently enabled for the given message level then the
        663 * given message is forwarded to all the registered output Handler objects.
        664 * @param {goog.debug.Loggable} msg The message to log.
        665 * @param {Error=} opt_exception An exception associated with the message.
        666 */
        667goog.debug.Logger.prototype.finer = function(msg, opt_exception) {
        668 if (goog.debug.LOGGING_ENABLED) {
        669 this.log(goog.debug.Logger.Level.FINER, msg, opt_exception);
        670 }
        671};
        672
        673
        674/**
        675 * Logs a message at the Logger.Level.FINEST level.
        676 * If the logger is currently enabled for the given message level then the
        677 * given message is forwarded to all the registered output Handler objects.
        678 * @param {goog.debug.Loggable} msg The message to log.
        679 * @param {Error=} opt_exception An exception associated with the message.
        680 */
        681goog.debug.Logger.prototype.finest = function(msg, opt_exception) {
        682 if (goog.debug.LOGGING_ENABLED) {
        683 this.log(goog.debug.Logger.Level.FINEST, msg, opt_exception);
        684 }
        685};
        686
        687
        688/**
        689 * Logs a LogRecord. If the logger is currently enabled for the
        690 * given message level then the given message is forwarded to all the
        691 * registered output Handler objects.
        692 * @param {goog.debug.LogRecord} logRecord A log record to log.
        693 */
        694goog.debug.Logger.prototype.logRecord = function(logRecord) {
        695 if (goog.debug.LOGGING_ENABLED && this.isLoggable(logRecord.getLevel())) {
        696 this.doLogRecord_(logRecord);
        697 }
        698};
        699
        700
        701/**
        702 * Logs a LogRecord.
        703 * @param {goog.debug.LogRecord} logRecord A log record to log.
        704 * @private
        705 */
        706goog.debug.Logger.prototype.doLogRecord_ = function(logRecord) {
        707 goog.debug.Logger.logToProfilers('log:' + logRecord.getMessage());
        708 if (goog.debug.Logger.ENABLE_HIERARCHY) {
        709 var target = this;
        710 while (target) {
        711 target.callPublish_(logRecord);
        712 target = target.getParent();
        713 }
        714 } else {
        715 for (var i = 0, handler; handler = goog.debug.Logger.rootHandlers_[i++]; ) {
        716 handler(logRecord);
        717 }
        718 }
        719};
        720
        721
        722/**
        723 * Calls the handlers for publish.
        724 * @param {goog.debug.LogRecord} logRecord The log record to publish.
        725 * @private
        726 */
        727goog.debug.Logger.prototype.callPublish_ = function(logRecord) {
        728 if (this.handlers_) {
        729 for (var i = 0, handler; handler = this.handlers_[i]; i++) {
        730 handler(logRecord);
        731 }
        732 }
        733};
        734
        735
        736/**
        737 * Sets the parent of this logger. This is used for setting up the logger tree.
        738 * @param {goog.debug.Logger} parent The parent logger.
        739 * @private
        740 */
        741goog.debug.Logger.prototype.setParent_ = function(parent) {
        742 this.parent_ = parent;
        743};
        744
        745
        746/**
        747 * Adds a child to this logger. This is used for setting up the logger tree.
        748 * @param {string} name The leaf name of the child.
        749 * @param {goog.debug.Logger} logger The child logger.
        750 * @private
        751 */
        752goog.debug.Logger.prototype.addChild_ = function(name, logger) {
        753 this.getChildren()[name] = logger;
        754};
        755
        756
        757/**
        758 * There is a single global LogManager object that is used to maintain a set of
        759 * shared state about Loggers and log services. This is loosely based on the
        760 * java class java.util.logging.LogManager.
        761 * @const
        762 */
        763goog.debug.LogManager = {};
        764
        765
        766/**
        767 * Map of logger names to logger objects.
        768 *
        769 * @type {!Object<string, !goog.debug.Logger>}
        770 * @private
        771 */
        772goog.debug.LogManager.loggers_ = {};
        773
        774
        775/**
        776 * The root logger which is the root of the logger tree.
        777 * @type {goog.debug.Logger}
        778 * @private
        779 */
        780goog.debug.LogManager.rootLogger_ = null;
        781
        782
        783/**
        784 * Initializes the LogManager if not already initialized.
        785 */
        786goog.debug.LogManager.initialize = function() {
        787 if (!goog.debug.LogManager.rootLogger_) {
        788 goog.debug.LogManager.rootLogger_ = new goog.debug.Logger(
        789 goog.debug.Logger.ROOT_LOGGER_NAME);
        790 goog.debug.LogManager.loggers_[goog.debug.Logger.ROOT_LOGGER_NAME] =
        791 goog.debug.LogManager.rootLogger_;
        792 goog.debug.LogManager.rootLogger_.setLevel(goog.debug.Logger.Level.CONFIG);
        793 }
        794};
        795
        796
        797/**
        798 * Returns all the loggers.
        799 * @return {!Object<string, !goog.debug.Logger>} Map of logger names to logger
        800 * objects.
        801 */
        802goog.debug.LogManager.getLoggers = function() {
        803 return goog.debug.LogManager.loggers_;
        804};
        805
        806
        807/**
        808 * Returns the root of the logger tree namespace, the logger with the empty
        809 * string as its name.
        810 *
        811 * @return {!goog.debug.Logger} The root logger.
        812 */
        813goog.debug.LogManager.getRoot = function() {
        814 goog.debug.LogManager.initialize();
        815 return /** @type {!goog.debug.Logger} */ (goog.debug.LogManager.rootLogger_);
        816};
        817
        818
        819/**
        820 * Finds a named logger.
        821 *
        822 * @param {string} name A name for the logger. This should be a dot-separated
        823 * name and should normally be based on the package name or class name of the
        824 * subsystem, such as goog.net.BrowserChannel.
        825 * @return {!goog.debug.Logger} The named logger.
        826 */
        827goog.debug.LogManager.getLogger = function(name) {
        828 goog.debug.LogManager.initialize();
        829 var ret = goog.debug.LogManager.loggers_[name];
        830 return ret || goog.debug.LogManager.createLogger_(name);
        831};
        832
        833
        834/**
        835 * Creates a function that can be passed to goog.debug.catchErrors. The function
        836 * will log all reported errors using the given logger.
        837 * @param {goog.debug.Logger=} opt_logger The logger to log the errors to.
        838 * Defaults to the root logger.
        839 * @return {function(Object)} The created function.
        840 */
        841goog.debug.LogManager.createFunctionForCatchErrors = function(opt_logger) {
        842 return function(info) {
        843 var logger = opt_logger || goog.debug.LogManager.getRoot();
        844 logger.severe('Error: ' + info.message + ' (' + info.fileName +
        845 ' @ Line: ' + info.line + ')');
        846 };
        847};
        848
        849
        850/**
        851 * Creates the named logger. Will also create the parents of the named logger
        852 * if they don't yet exist.
        853 * @param {string} name The name of the logger.
        854 * @return {!goog.debug.Logger} The named logger.
        855 * @private
        856 */
        857goog.debug.LogManager.createLogger_ = function(name) {
        858 // find parent logger
        859 var logger = new goog.debug.Logger(name);
        860 if (goog.debug.Logger.ENABLE_HIERARCHY) {
        861 var lastDotIndex = name.lastIndexOf('.');
        862 var parentName = name.substr(0, lastDotIndex);
        863 var leafName = name.substr(lastDotIndex + 1);
        864 var parentLogger = goog.debug.LogManager.getLogger(parentName);
        865
        866 // tell the parent about the child and the child about the parent
        867 parentLogger.addChild_(leafName, logger);
        868 logger.setParent_(parentLogger);
        869 }
        870
        871 goog.debug.LogManager.loggers_[name] = logger;
        872 return logger;
        873};
        \ No newline at end of file diff --git a/docs/source/lib/goog/debug/logrecord.js.src.html b/docs/source/lib/goog/debug/logrecord.js.src.html new file mode 100644 index 0000000..fc8deb0 --- /dev/null +++ b/docs/source/lib/goog/debug/logrecord.js.src.html @@ -0,0 +1 @@ +logrecord.js

        lib/goog/debug/logrecord.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 the LogRecord class. Please minimize
        17 * dependencies this file has on other closure classes as any dependency it
        18 * takes won't be able to use the logging infrastructure.
        19 *
        20 */
        21
        22goog.provide('goog.debug.LogRecord');
        23
        24
        25
        26/**
        27 * LogRecord objects are used to pass logging requests between
        28 * the logging framework and individual log Handlers.
        29 * @constructor
        30 * @param {goog.debug.Logger.Level} level One of the level identifiers.
        31 * @param {string} msg The string message.
        32 * @param {string} loggerName The name of the source logger.
        33 * @param {number=} opt_time Time this log record was created if other than now.
        34 * If 0, we use #goog.now.
        35 * @param {number=} opt_sequenceNumber Sequence number of this log record. This
        36 * should only be passed in when restoring a log record from persistence.
        37 */
        38goog.debug.LogRecord = function(level, msg, loggerName,
        39 opt_time, opt_sequenceNumber) {
        40 this.reset(level, msg, loggerName, opt_time, opt_sequenceNumber);
        41};
        42
        43
        44/**
        45 * Time the LogRecord was created.
        46 * @type {number}
        47 * @private
        48 */
        49goog.debug.LogRecord.prototype.time_;
        50
        51
        52/**
        53 * Level of the LogRecord
        54 * @type {goog.debug.Logger.Level}
        55 * @private
        56 */
        57goog.debug.LogRecord.prototype.level_;
        58
        59
        60/**
        61 * Message associated with the record
        62 * @type {string}
        63 * @private
        64 */
        65goog.debug.LogRecord.prototype.msg_;
        66
        67
        68/**
        69 * Name of the logger that created the record.
        70 * @type {string}
        71 * @private
        72 */
        73goog.debug.LogRecord.prototype.loggerName_;
        74
        75
        76/**
        77 * Sequence number for the LogRecord. Each record has a unique sequence number
        78 * that is greater than all log records created before it.
        79 * @type {number}
        80 * @private
        81 */
        82goog.debug.LogRecord.prototype.sequenceNumber_ = 0;
        83
        84
        85/**
        86 * Exception associated with the record
        87 * @type {Object}
        88 * @private
        89 */
        90goog.debug.LogRecord.prototype.exception_ = null;
        91
        92
        93/**
        94 * @define {boolean} Whether to enable log sequence numbers.
        95 */
        96goog.define('goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS', true);
        97
        98
        99/**
        100 * A sequence counter for assigning increasing sequence numbers to LogRecord
        101 * objects.
        102 * @type {number}
        103 * @private
        104 */
        105goog.debug.LogRecord.nextSequenceNumber_ = 0;
        106
        107
        108/**
        109 * Sets all fields of the log record.
        110 * @param {goog.debug.Logger.Level} level One of the level identifiers.
        111 * @param {string} msg The string message.
        112 * @param {string} loggerName The name of the source logger.
        113 * @param {number=} opt_time Time this log record was created if other than now.
        114 * If 0, we use #goog.now.
        115 * @param {number=} opt_sequenceNumber Sequence number of this log record. This
        116 * should only be passed in when restoring a log record from persistence.
        117 */
        118goog.debug.LogRecord.prototype.reset = function(level, msg, loggerName,
        119 opt_time, opt_sequenceNumber) {
        120 if (goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS) {
        121 this.sequenceNumber_ = typeof opt_sequenceNumber == 'number' ?
        122 opt_sequenceNumber : goog.debug.LogRecord.nextSequenceNumber_++;
        123 }
        124
        125 this.time_ = opt_time || goog.now();
        126 this.level_ = level;
        127 this.msg_ = msg;
        128 this.loggerName_ = loggerName;
        129 delete this.exception_;
        130};
        131
        132
        133/**
        134 * Get the source Logger's name.
        135 *
        136 * @return {string} source logger name (may be null).
        137 */
        138goog.debug.LogRecord.prototype.getLoggerName = function() {
        139 return this.loggerName_;
        140};
        141
        142
        143/**
        144 * Get the exception that is part of the log record.
        145 *
        146 * @return {Object} the exception.
        147 */
        148goog.debug.LogRecord.prototype.getException = function() {
        149 return this.exception_;
        150};
        151
        152
        153/**
        154 * Set the exception that is part of the log record.
        155 *
        156 * @param {Object} exception the exception.
        157 */
        158goog.debug.LogRecord.prototype.setException = function(exception) {
        159 this.exception_ = exception;
        160};
        161
        162
        163/**
        164 * Get the source Logger's name.
        165 *
        166 * @param {string} loggerName source logger name (may be null).
        167 */
        168goog.debug.LogRecord.prototype.setLoggerName = function(loggerName) {
        169 this.loggerName_ = loggerName;
        170};
        171
        172
        173/**
        174 * Get the logging message level, for example Level.SEVERE.
        175 * @return {goog.debug.Logger.Level} the logging message level.
        176 */
        177goog.debug.LogRecord.prototype.getLevel = function() {
        178 return this.level_;
        179};
        180
        181
        182/**
        183 * Set the logging message level, for example Level.SEVERE.
        184 * @param {goog.debug.Logger.Level} level the logging message level.
        185 */
        186goog.debug.LogRecord.prototype.setLevel = function(level) {
        187 this.level_ = level;
        188};
        189
        190
        191/**
        192 * Get the "raw" log message, before localization or formatting.
        193 *
        194 * @return {string} the raw message string.
        195 */
        196goog.debug.LogRecord.prototype.getMessage = function() {
        197 return this.msg_;
        198};
        199
        200
        201/**
        202 * Set the "raw" log message, before localization or formatting.
        203 *
        204 * @param {string} msg the raw message string.
        205 */
        206goog.debug.LogRecord.prototype.setMessage = function(msg) {
        207 this.msg_ = msg;
        208};
        209
        210
        211/**
        212 * Get event time in milliseconds since 1970.
        213 *
        214 * @return {number} event time in millis since 1970.
        215 */
        216goog.debug.LogRecord.prototype.getMillis = function() {
        217 return this.time_;
        218};
        219
        220
        221/**
        222 * Set event time in milliseconds since 1970.
        223 *
        224 * @param {number} time event time in millis since 1970.
        225 */
        226goog.debug.LogRecord.prototype.setMillis = function(time) {
        227 this.time_ = time;
        228};
        229
        230
        231/**
        232 * Get the sequence number.
        233 * <p>
        234 * Sequence numbers are normally assigned in the LogRecord
        235 * constructor, which assigns unique sequence numbers to
        236 * each new LogRecord in increasing order.
        237 * @return {number} the sequence number.
        238 */
        239goog.debug.LogRecord.prototype.getSequenceNumber = function() {
        240 return this.sequenceNumber_;
        241};
        242
        \ No newline at end of file diff --git a/docs/source/lib/goog/deps.js.src.html b/docs/source/lib/goog/deps.js.src.html index c3c1eac..9a40185 100644 --- a/docs/source/lib/goog/deps.js.src.html +++ b/docs/source/lib/goog/deps.js.src.html @@ -1 +1 @@ -deps.js

        lib/goog/deps.js

        1// This file has been auto-generated; do not edit by hand
        2goog.addDependency("../atoms/error.js", ["bot.Error","bot.ErrorCode"], []);
        3goog.addDependency("../atoms/response.js", ["bot.response","bot.response.ResponseObject"], ["bot.Error","bot.ErrorCode"]);
        4goog.addDependency("../atoms/json.js", ["bot.json"], ["bot.userAgent","goog.json","goog.userAgent"]);
        5goog.addDependency("../atoms/userAgent.js", ["bot.userAgent"], ["goog.string","goog.userAgent","goog.userAgent.product","goog.userAgent.product.isVersion"]);
        6goog.addDependency("string/string.js", ["goog.string","goog.string.Unicode"], []);
        7goog.addDependency("useragent/useragent.js", ["goog.userAgent"], ["goog.string"]);
        8goog.addDependency("useragent/product.js", ["goog.userAgent.product"], ["goog.userAgent"]);
        9goog.addDependency("useragent/product_isversion.js", ["goog.userAgent.product.isVersion"], ["goog.userAgent.product"]);
        10goog.addDependency("json/json.js", ["goog.json","goog.json.Serializer"], []);
        11goog.addDependency("../webdriver/capabilities.js", ["webdriver.Browser","webdriver.Capabilities","webdriver.Capability"], []);
        12goog.addDependency("../webdriver/logging.js", ["webdriver.logging","webdriver.logging.Preferences"], ["goog.object"]);
        13goog.addDependency("object/object.js", ["goog.object"], []);
        14goog.addDependency("../webdriver/actionsequence.js", ["webdriver.ActionSequence"], ["goog.array","webdriver.Button","webdriver.Command","webdriver.CommandName","webdriver.Key"]);
        15goog.addDependency("array/array.js", ["goog.array","goog.array.ArrayLike"], ["goog.asserts"]);
        16goog.addDependency("asserts/asserts.js", ["goog.asserts","goog.asserts.AssertionError"], ["goog.debug.Error","goog.string"]);
        17goog.addDependency("debug/error.js", ["goog.debug.Error"], []);
        18goog.addDependency("../webdriver/button.js", ["webdriver.Button"], []);
        19goog.addDependency("../webdriver/command.js", ["webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName"], []);
        20goog.addDependency("../webdriver/key.js", ["webdriver.Key"], []);
        21goog.addDependency("../webdriver/stacktrace.js", ["webdriver.stacktrace","webdriver.stacktrace.Snapshot"], ["goog.array","goog.string","goog.userAgent"]);
        22goog.addDependency("../webdriver/locators.js", ["webdriver.By","webdriver.Locator","webdriver.Locator.Strategy"], ["goog.array","goog.object","goog.string"]);
        23goog.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"]);
        24goog.addDependency("../webdriver/events.js", ["webdriver.EventEmitter"], []);
        25goog.addDependency("../webdriver/process.js", ["webdriver.process"], ["goog.Uri","goog.array","goog.json"]);
        26goog.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"]);
        27goog.addDependency("structs/structs.js", ["goog.structs"], ["goog.array","goog.object"]);
        28goog.addDependency("structs/map.js", ["goog.structs.Map"], ["goog.iter.Iterator","goog.iter.StopIteration","goog.object"]);
        29goog.addDependency("iter/iter.js", ["goog.iter","goog.iter.Iterator","goog.iter.StopIteration"], ["goog.array","goog.asserts"]);
        30goog.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"]);
        31goog.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"]);
        32goog.addDependency("../webdriver/session.js", ["webdriver.Session"], ["webdriver.Capabilities"]);
        33goog.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"]);
        34goog.addDependency("../webdriver/abstractbuilder.js", ["webdriver.AbstractBuilder"], ["webdriver.Capabilities","webdriver.process"]);
        35goog.addDependency("../webdriver/firefoxdomexecutor.js", ["webdriver.FirefoxDomExecutor"], ["bot.response","goog.json","goog.userAgent.product","webdriver.Command","webdriver.CommandName"]);
        36goog.addDependency("../webdriver/http/corsclient.js", ["webdriver.http.CorsClient"], ["goog.json","webdriver.http.Response"]);
        37goog.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"]);
        38goog.addDependency("../webdriver/http/xhrclient.js", ["webdriver.http.XhrClient"], ["goog.json","goog.net.XmlHttp","webdriver.http.Response"]);
        39goog.addDependency("net/xmlhttp.js", ["goog.net.DefaultXmlHttpFactory","goog.net.XmlHttp","goog.net.XmlHttp.OptionType","goog.net.XmlHttp.ReadyState"], ["goog.net.WrapperXmlHttpFactory","goog.net.XmlHttpFactory"]);
        40goog.addDependency("net/wrapperxmlhttpfactory.js", ["goog.net.WrapperXmlHttpFactory"], ["goog.net.XmlHttpFactory"]);
        41goog.addDependency("net/xmlhttpfactory.js", ["goog.net.XmlHttpFactory"], []);
        42goog.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"]);
        43goog.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"]);
        44goog.addDependency("labs/testing/matcher.js", ["goog.labs.testing.Matcher"], []);
        45goog.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"]);
        46goog.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"]);
        47goog.addDependency("labs/testing/logicmatcher.js", ["goog.labs.testing.AllOfMatcher","goog.labs.testing.AnyOfMatcher","goog.labs.testing.IsNotMatcher"], ["goog.array","goog.labs.testing.Matcher"]);
        48goog.addDependency("labs/testing/assertthat.js", ["goog.labs.testing.MatcherError","goog.labs.testing.assertThat"], ["goog.asserts","goog.debug.Error","goog.labs.testing.Matcher"]);
        \ No newline at end of file +deps.js

        lib/goog/deps.js

        1// This file has been auto-generated; do not edit by hand
        2goog.addDependency("../webdriver/test/builder_test.js", [], ["goog.testing.jsunit","webdriver.Builder"], false);
        3goog.addDependency("testing/jsunit.js", ["goog.testing.jsunit"], ["goog.dom.TagName","goog.testing.TestCase","goog.testing.TestRunner"], false);
        4goog.addDependency("dom/tagname.js", ["goog.dom.TagName"], [], false);
        5goog.addDependency("testing/testcase.js", ["goog.testing.TestCase","goog.testing.TestCase.Error","goog.testing.TestCase.Order","goog.testing.TestCase.Result","goog.testing.TestCase.Test"], ["goog.Promise","goog.Thenable","goog.asserts","goog.dom.TagName","goog.object","goog.testing.asserts","goog.testing.stacktrace"], false);
        6goog.addDependency("promise/promise.js", ["goog.Promise"], ["goog.Thenable","goog.asserts","goog.async.FreeList","goog.async.run","goog.async.throwException","goog.debug.Error","goog.promise.Resolver"], false);
        7goog.addDependency("promise/thenable.js", ["goog.Thenable"], [], false);
        8goog.addDependency("asserts/asserts.js", ["goog.asserts","goog.asserts.AssertionError"], ["goog.debug.Error","goog.dom.NodeType","goog.string"], false);
        9goog.addDependency("debug/error.js", ["goog.debug.Error"], [], false);
        10goog.addDependency("dom/nodetype.js", ["goog.dom.NodeType"], [], false);
        11goog.addDependency("string/string.js", ["goog.string","goog.string.Unicode"], [], false);
        12goog.addDependency("async/freelist.js", ["goog.async.FreeList"], [], false);
        13goog.addDependency("async/run.js", ["goog.async.run"], ["goog.async.WorkQueue","goog.async.nextTick","goog.async.throwException"], false);
        14goog.addDependency("async/workqueue.js", ["goog.async.WorkItem","goog.async.WorkQueue"], ["goog.asserts","goog.async.FreeList"], false);
        15goog.addDependency("async/nexttick.js", ["goog.async.nextTick","goog.async.throwException"], ["goog.debug.entryPointRegistry","goog.dom.TagName","goog.functions","goog.labs.userAgent.browser","goog.labs.userAgent.engine"], false);
        16goog.addDependency("debug/entrypointregistry.js", ["goog.debug.EntryPointMonitor","goog.debug.entryPointRegistry"], ["goog.asserts"], false);
        17goog.addDependency("functions/functions.js", ["goog.functions"], [], false);
        18goog.addDependency("labs/useragent/browser.js", ["goog.labs.userAgent.browser"], ["goog.array","goog.labs.userAgent.util","goog.object","goog.string"], false);
        19goog.addDependency("array/array.js", ["goog.array","goog.array.ArrayLike"], ["goog.asserts"], false);
        20goog.addDependency("labs/useragent/util.js", ["goog.labs.userAgent.util"], ["goog.string"], false);
        21goog.addDependency("object/object.js", ["goog.object"], [], false);
        22goog.addDependency("labs/useragent/engine.js", ["goog.labs.userAgent.engine"], ["goog.array","goog.labs.userAgent.util","goog.string"], false);
        23goog.addDependency("promise/resolver.js", ["goog.promise.Resolver"], [], false);
        24goog.addDependency("testing/asserts.js", ["goog.testing.JsUnitException","goog.testing.asserts","goog.testing.asserts.ArrayLike"], ["goog.testing.stacktrace"], false);
        25goog.addDependency("testing/stacktrace.js", ["goog.testing.stacktrace","goog.testing.stacktrace.Frame"], [], false);
        26goog.addDependency("testing/testrunner.js", ["goog.testing.TestRunner"], ["goog.dom.TagName","goog.testing.TestCase"], false);
        27goog.addDependency("../webdriver/builder.js", ["webdriver.Builder"], ["goog.Uri","goog.userAgent","webdriver.Capabilities","webdriver.FirefoxDomExecutor","webdriver.WebDriver","webdriver.http.CorsClient","webdriver.http.Executor","webdriver.http.XhrClient"], false);
        28goog.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"], false);
        29goog.addDependency("structs/structs.js", ["goog.structs"], ["goog.array","goog.object"], false);
        30goog.addDependency("structs/map.js", ["goog.structs.Map"], ["goog.iter.Iterator","goog.iter.StopIteration","goog.object"], false);
        31goog.addDependency("iter/iter.js", ["goog.iter","goog.iter.Iterable","goog.iter.Iterator","goog.iter.StopIteration"], ["goog.array","goog.asserts","goog.functions","goog.math"], false);
        32goog.addDependency("math/math.js", ["goog.math"], ["goog.array","goog.asserts"], false);
        33goog.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"], false);
        34goog.addDependency("useragent/useragent.js", ["goog.userAgent"], ["goog.labs.userAgent.browser","goog.labs.userAgent.engine","goog.labs.userAgent.platform","goog.labs.userAgent.util","goog.string"], false);
        35goog.addDependency("labs/useragent/platform.js", ["goog.labs.userAgent.platform"], ["goog.labs.userAgent.util","goog.string"], false);
        36goog.addDependency("../webdriver/capabilities.js", ["webdriver.Browser","webdriver.Capabilities","webdriver.Capability","webdriver.ProxyConfig"], ["webdriver.Serializable","webdriver.logging"], false);
        37goog.addDependency("../webdriver/serializable.js", ["webdriver.Serializable"], [], false);
        38goog.addDependency("../webdriver/logging.js", ["webdriver.logging"], ["goog.debug.LogManager","goog.debug.LogRecord","goog.debug.Logger","goog.object"], true);
        39goog.addDependency("debug/logger.js", ["goog.debug.LogManager","goog.debug.Loggable","goog.debug.Logger","goog.debug.Logger.Level"], ["goog.array","goog.asserts","goog.debug","goog.debug.LogBuffer","goog.debug.LogRecord"], false);
        40goog.addDependency("debug/debug.js", ["goog.debug"], ["goog.array","goog.html.SafeHtml","goog.html.SafeUrl","goog.html.uncheckedconversions","goog.string.Const","goog.structs.Set","goog.userAgent"], false);
        41goog.addDependency("html/safehtml.js", ["goog.html.SafeHtml"], ["goog.array","goog.asserts","goog.dom.TagName","goog.dom.tags","goog.html.SafeStyle","goog.html.SafeStyleSheet","goog.html.SafeUrl","goog.html.TrustedResourceUrl","goog.i18n.bidi.Dir","goog.i18n.bidi.DirectionalString","goog.object","goog.string","goog.string.Const","goog.string.TypedString"], false);
        42goog.addDependency("dom/tags.js", ["goog.dom.tags"], ["goog.object"], false);
        43goog.addDependency("html/safestyle.js", ["goog.html.SafeStyle"], ["goog.array","goog.asserts","goog.string","goog.string.Const","goog.string.TypedString"], false);
        44goog.addDependency("string/const.js", ["goog.string.Const"], ["goog.asserts","goog.string.TypedString"], false);
        45goog.addDependency("string/typedstring.js", ["goog.string.TypedString"], [], false);
        46goog.addDependency("html/safestylesheet.js", ["goog.html.SafeStyleSheet"], ["goog.array","goog.asserts","goog.string","goog.string.Const","goog.string.TypedString"], false);
        47goog.addDependency("html/safeurl.js", ["goog.html.SafeUrl"], ["goog.asserts","goog.fs.url","goog.i18n.bidi.Dir","goog.i18n.bidi.DirectionalString","goog.string.Const","goog.string.TypedString"], false);
        48goog.addDependency("fs/url.js", ["goog.fs.url"], [], false);
        49goog.addDependency("i18n/bidi.js", ["goog.i18n.bidi","goog.i18n.bidi.Dir","goog.i18n.bidi.DirectionalString","goog.i18n.bidi.Format"], [], false);
        50goog.addDependency("html/trustedresourceurl.js", ["goog.html.TrustedResourceUrl"], ["goog.asserts","goog.i18n.bidi.Dir","goog.i18n.bidi.DirectionalString","goog.string.Const","goog.string.TypedString"], false);
        51goog.addDependency("html/uncheckedconversions.js", ["goog.html.uncheckedconversions"], ["goog.asserts","goog.html.SafeHtml","goog.html.SafeScript","goog.html.SafeStyle","goog.html.SafeStyleSheet","goog.html.SafeUrl","goog.html.TrustedResourceUrl","goog.string","goog.string.Const"], false);
        52goog.addDependency("html/safescript.js", ["goog.html.SafeScript"], ["goog.asserts","goog.string.Const","goog.string.TypedString"], false);
        53goog.addDependency("structs/set.js", ["goog.structs.Set"], ["goog.structs","goog.structs.Collection","goog.structs.Map"], false);
        54goog.addDependency("structs/collection.js", ["goog.structs.Collection"], [], false);
        55goog.addDependency("debug/logbuffer.js", ["goog.debug.LogBuffer"], ["goog.asserts","goog.debug.LogRecord"], false);
        56goog.addDependency("debug/logrecord.js", ["goog.debug.LogRecord"], [], false);
        57goog.addDependency("../webdriver/firefoxdomexecutor.js", ["webdriver.FirefoxDomExecutor"], ["bot.response","goog.userAgent.product","webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName"], false);
        58goog.addDependency("../atoms/response.js", ["bot.response","bot.response.ResponseObject"], ["bot.Error","bot.ErrorCode"], false);
        59goog.addDependency("../atoms/error.js", ["bot.Error","bot.ErrorCode"], [], false);
        60goog.addDependency("useragent/product.js", ["goog.userAgent.product"], ["goog.labs.userAgent.browser","goog.labs.userAgent.platform","goog.userAgent"], false);
        61goog.addDependency("../webdriver/command.js", ["webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName"], [], false);
        62goog.addDependency("../webdriver/webdriver.js", ["webdriver.Alert","webdriver.AlertPromise","webdriver.FileDetector","webdriver.UnhandledAlertError","webdriver.WebDriver","webdriver.WebElement","webdriver.WebElementPromise"], ["bot.Error","bot.ErrorCode","bot.response","goog.array","goog.object","webdriver.ActionSequence","webdriver.Command","webdriver.CommandName","webdriver.Key","webdriver.Locator","webdriver.Serializable","webdriver.Session","webdriver.TouchSequence","webdriver.logging","webdriver.promise","webdriver.until"], false);
        63goog.addDependency("../webdriver/actionsequence.js", ["webdriver.ActionSequence"], ["goog.array","webdriver.Button","webdriver.Command","webdriver.CommandName","webdriver.Key"], false);
        64goog.addDependency("../webdriver/button.js", ["webdriver.Button"], [], false);
        65goog.addDependency("../webdriver/key.js", ["webdriver.Key"], [], false);
        66goog.addDependency("../webdriver/locators.js", ["webdriver.By","webdriver.Locator","webdriver.Locator.Strategy"], ["goog.array","goog.object","goog.string"], false);
        67goog.addDependency("../webdriver/session.js", ["webdriver.Session"], ["webdriver.Capabilities"], false);
        68goog.addDependency("../webdriver/touchsequence.js", ["webdriver.TouchSequence"], ["goog.array","webdriver.Command","webdriver.CommandName"], false);
        69goog.addDependency("../webdriver/promise.js", ["webdriver.promise"], ["goog.array","goog.asserts","goog.async.run","goog.async.throwException","goog.debug.Error","goog.log","goog.object","webdriver.EventEmitter","webdriver.stacktrace"], true);
        70goog.addDependency("log/log.js", ["goog.log","goog.log.Level","goog.log.LogRecord","goog.log.Logger"], ["goog.debug","goog.debug.LogManager","goog.debug.LogRecord","goog.debug.Logger"], false);
        71goog.addDependency("../webdriver/events.js", ["webdriver.EventEmitter"], [], false);
        72goog.addDependency("../webdriver/stacktrace.js", ["webdriver.stacktrace","webdriver.stacktrace.Snapshot"], ["goog.array","goog.string","goog.userAgent"], false);
        73goog.addDependency("../webdriver/until.js", ["webdriver.until"], ["bot.ErrorCode","goog.array","goog.string","webdriver.Locator"], false);
        74goog.addDependency("../webdriver/http/corsclient.js", ["webdriver.http.CorsClient"], ["webdriver.http.Client","webdriver.http.Response"], false);
        75goog.addDependency("../webdriver/http/http.js", ["webdriver.http.Client","webdriver.http.Executor","webdriver.http.Request","webdriver.http.Response"], ["bot.ErrorCode","goog.array","webdriver.CommandExecutor","webdriver.CommandName","webdriver.logging","webdriver.promise"], false);
        76goog.addDependency("../webdriver/http/xhrclient.js", ["webdriver.http.XhrClient"], ["goog.net.XmlHttp","webdriver.http.Client","webdriver.http.Response"], false);
        77goog.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"], false);
        78goog.addDependency("net/wrapperxmlhttpfactory.js", ["goog.net.WrapperXmlHttpFactory"], ["goog.net.XhrLike","goog.net.XmlHttpFactory"], false);
        79goog.addDependency("net/xhrlike.js", ["goog.net.XhrLike"], [], false);
        80goog.addDependency("net/xmlhttpfactory.js", ["goog.net.XmlHttpFactory"], ["goog.net.XhrLike"], false);
        81goog.addDependency("../webdriver/test/capabilities_test.js", [], ["goog.testing.jsunit","webdriver.Capabilities"], false);
        82goog.addDependency("../webdriver/test/events_test.js", [], ["goog.testing.jsunit","webdriver.EventEmitter"], false);
        83goog.addDependency("../webdriver/test/locators_test.js", [], ["goog.testing.jsunit","webdriver.By","webdriver.Locator","webdriver.Locator.Strategy","webdriver.test.testutil"], false);
        84goog.addDependency("../webdriver/test/testutil.js", ["webdriver.test.testutil","webdriver.test.testutil.StubError"], ["goog.array","goog.debug.Error","goog.string","goog.testing.recordFunction","webdriver.stacktrace"], false);
        85goog.addDependency("testing/recordfunction.js", ["goog.testing.FunctionCall","goog.testing.recordConstructor","goog.testing.recordFunction"], ["goog.testing.asserts"], false);
        86goog.addDependency("../webdriver/test/logging_test.js", [], ["goog.debug.LogRecord","goog.debug.Logger","goog.testing.jsunit","webdriver.logging"], false);
        87goog.addDependency("../webdriver/test/promise_error_test.js", [], ["goog.Promise","goog.async.run","goog.testing.jsunit","goog.userAgent","goog.userAgent.product","webdriver.promise","webdriver.test.testutil"], false);
        88goog.addDependency("../webdriver/test/promise_flow_test.js", [], ["goog.array","goog.string","goog.testing.jsunit","goog.userAgent","webdriver.promise","webdriver.stacktrace.Snapshot","webdriver.stacktrace","webdriver.test.testutil"], false);
        89goog.addDependency("../webdriver/test/promise_test.js", [], ["goog.testing.MockClock","goog.testing.jsunit","goog.userAgent","webdriver.promise","webdriver.stacktrace","webdriver.test.testutil"], false);
        90goog.addDependency("testing/mockclock.js", ["goog.testing.MockClock"], ["goog.Disposable","goog.async.run","goog.testing.PropertyReplacer","goog.testing.events","goog.testing.events.Event"], false);
        91goog.addDependency("disposable/disposable.js", ["goog.Disposable","goog.dispose","goog.disposeAll"], ["goog.disposable.IDisposable"], false);
        92goog.addDependency("disposable/idisposable.js", ["goog.disposable.IDisposable"], [], false);
        93goog.addDependency("testing/propertyreplacer.js", ["goog.testing.PropertyReplacer"], ["goog.testing.ObjectPropertyString","goog.userAgent"], false);
        94goog.addDependency("testing/objectpropertystring.js", ["goog.testing.ObjectPropertyString"], [], false);
        95goog.addDependency("testing/events/events.js", ["goog.testing.events","goog.testing.events.Event"], ["goog.Disposable","goog.asserts","goog.dom.NodeType","goog.events","goog.events.BrowserEvent","goog.events.BrowserFeature","goog.events.EventTarget","goog.events.EventType","goog.events.KeyCodes","goog.object","goog.style","goog.userAgent"], false);
        96goog.addDependency("events/events.js", ["goog.events","goog.events.CaptureSimulationMode","goog.events.Key","goog.events.ListenableType"], ["goog.asserts","goog.debug.entryPointRegistry","goog.events.BrowserEvent","goog.events.BrowserFeature","goog.events.Listenable","goog.events.ListenerMap"], false);
        97goog.addDependency("events/browserevent.js", ["goog.events.BrowserEvent","goog.events.BrowserEvent.MouseButton"], ["goog.events.BrowserFeature","goog.events.Event","goog.events.EventType","goog.reflect","goog.userAgent"], false);
        98goog.addDependency("events/browserfeature.js", ["goog.events.BrowserFeature"], ["goog.userAgent"], false);
        99goog.addDependency("events/event.js", ["goog.events.Event","goog.events.EventLike"], ["goog.Disposable","goog.events.EventId"], false);
        100goog.addDependency("events/eventid.js", ["goog.events.EventId"], [], false);
        101goog.addDependency("events/eventtype.js", ["goog.events.EventType"], ["goog.userAgent"], false);
        102goog.addDependency("reflect/reflect.js", ["goog.reflect"], [], false);
        103goog.addDependency("events/listenable.js", ["goog.events.Listenable","goog.events.ListenableKey"], ["goog.events.EventId"], false);
        104goog.addDependency("events/listenermap.js", ["goog.events.ListenerMap"], ["goog.array","goog.events.Listener","goog.object"], false);
        105goog.addDependency("events/listener.js", ["goog.events.Listener"], ["goog.events.ListenableKey"], false);
        106goog.addDependency("events/eventtarget.js", ["goog.events.EventTarget"], ["goog.Disposable","goog.asserts","goog.events","goog.events.Event","goog.events.Listenable","goog.events.ListenerMap","goog.object"], false);
        107goog.addDependency("events/keycodes.js", ["goog.events.KeyCodes"], ["goog.userAgent"], false);
        108goog.addDependency("style/style.js", ["goog.style"], ["goog.array","goog.asserts","goog.dom","goog.dom.NodeType","goog.dom.TagName","goog.dom.vendor","goog.math.Box","goog.math.Coordinate","goog.math.Rect","goog.math.Size","goog.object","goog.string","goog.userAgent"], false);
        109goog.addDependency("dom/dom.js", ["goog.dom","goog.dom.Appendable","goog.dom.DomHelper"], ["goog.array","goog.asserts","goog.dom.BrowserFeature","goog.dom.NodeType","goog.dom.TagName","goog.dom.safe","goog.html.SafeHtml","goog.math.Coordinate","goog.math.Size","goog.object","goog.string","goog.string.Unicode","goog.userAgent"], false);
        110goog.addDependency("dom/browserfeature.js", ["goog.dom.BrowserFeature"], ["goog.userAgent"], false);
        111goog.addDependency("dom/safe.js", ["goog.dom.safe","goog.dom.safe.InsertAdjacentHtmlPosition"], ["goog.asserts","goog.html.SafeHtml","goog.html.SafeUrl","goog.html.TrustedResourceUrl","goog.string","goog.string.Const"], false);
        112goog.addDependency("math/coordinate.js", ["goog.math.Coordinate"], ["goog.math"], false);
        113goog.addDependency("math/size.js", ["goog.math.Size"], [], false);
        114goog.addDependency("dom/vendor.js", ["goog.dom.vendor"], ["goog.string","goog.userAgent"], false);
        115goog.addDependency("math/box.js", ["goog.math.Box"], ["goog.math.Coordinate"], false);
        116goog.addDependency("math/rect.js", ["goog.math.Rect"], ["goog.math.Box","goog.math.Coordinate","goog.math.Size"], false);
        117goog.addDependency("../webdriver/test/stacktrace_test.js", [], ["bot.Error","bot.ErrorCode","goog.string","goog.testing.JsUnitException","goog.testing.PropertyReplacer","goog.testing.StrictMock","goog.testing.jsunit","goog.testing.stacktrace","webdriver.stacktrace","webdriver.test.testutil"], false);
        118goog.addDependency("testing/strictmock.js", ["goog.testing.StrictMock"], ["goog.array","goog.testing.Mock"], false);
        119goog.addDependency("testing/mock.js", ["goog.testing.Mock","goog.testing.MockExpectation"], ["goog.array","goog.object","goog.testing.JsUnitException","goog.testing.MockInterface","goog.testing.mockmatchers"], false);
        120goog.addDependency("testing/mockinterface.js", ["goog.testing.MockInterface"], [], false);
        121goog.addDependency("testing/mockmatchers.js", ["goog.testing.mockmatchers","goog.testing.mockmatchers.ArgumentMatcher","goog.testing.mockmatchers.IgnoreArgument","goog.testing.mockmatchers.InstanceOf","goog.testing.mockmatchers.ObjectEquals","goog.testing.mockmatchers.RegexpMatch","goog.testing.mockmatchers.SaveArgument","goog.testing.mockmatchers.TypeOf"], ["goog.array","goog.dom","goog.testing.asserts"], false);
        122goog.addDependency("../webdriver/test/test_bootstrap.js", [], [], false);
        123goog.addDependency("../webdriver/test/testutil_test.js", [], ["goog.testing.jsunit","webdriver.test.testutil"], false);
        124goog.addDependency("../webdriver/test/webdriver_test.js", [], ["bot.ErrorCode","goog.Promise","goog.functions","goog.testing.PropertyReplacer","goog.testing.MockControl","goog.testing.jsunit","goog.userAgent","webdriver.Capabilities","webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName","webdriver.FileDetector","webdriver.WebDriver","webdriver.Serializable","webdriver.Session","webdriver.logging","webdriver.promise","webdriver.test.testutil"], false);
        125goog.addDependency("testing/mockcontrol.js", ["goog.testing.MockControl"], ["goog.array","goog.testing","goog.testing.LooseMock","goog.testing.StrictMock"], false);
        126goog.addDependency("testing/functionmock.js", ["goog.testing","goog.testing.FunctionMock","goog.testing.GlobalFunctionMock","goog.testing.MethodMock"], ["goog.object","goog.testing.LooseMock","goog.testing.Mock","goog.testing.PropertyReplacer","goog.testing.StrictMock"], false);
        127goog.addDependency("testing/loosemock.js", ["goog.testing.LooseExpectationCollection","goog.testing.LooseMock"], ["goog.array","goog.structs.Map","goog.testing.Mock"], false);
        128goog.addDependency("../webdriver/test/http/corsclient_test.js", [], ["goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.jsunit","goog.userAgent","webdriver.http.CorsClient","webdriver.http.Request","webdriver.test.testutil"], false);
        129goog.addDependency("../webdriver/test/http/http_test.js", [], ["bot.ErrorCode","goog.Uri","goog.testing.MockControl","goog.testing.jsunit","goog.userAgent","webdriver.Command","webdriver.http.Client","webdriver.http.Executor","webdriver.promise","webdriver.test.testutil"], false);
        130goog.addDependency("../webdriver/test/http/xhrclient_test.js", [], ["goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.jsunit","goog.userAgent","webdriver.http.Request","webdriver.http.XhrClient","webdriver.promise","webdriver.test.testutil"], false);
        131goog.addDependency("../webdriver/test/testing/asserts_test.js", [], ["goog.testing.jsunit","goog.userAgent","webdriver.test.testutil","webdriver.testing.assert","webdriver.testing.asserts"], false);
        132goog.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"], false);
        133goog.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"], false);
        134goog.addDependency("labs/testing/matcher.js", ["goog.labs.testing.Matcher"], [], false);
        135goog.addDependency("labs/testing/stringmatcher.js", ["goog.labs.testing.ContainsStringMatcher","goog.labs.testing.EndsWithMatcher","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"], false);
        136goog.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"], false);
        137goog.addDependency("labs/testing/logicmatcher.js", ["goog.labs.testing.AllOfMatcher","goog.labs.testing.AnyOfMatcher","goog.labs.testing.IsNotMatcher"], ["goog.array","goog.labs.testing.Matcher"], false);
        138goog.addDependency("labs/testing/assertthat.js", ["goog.labs.testing.MatcherError","goog.labs.testing.assertThat"], ["goog.debug.Error"], false);
        139goog.addDependency("../webdriver/test/testing/client_test.js", [], ["goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.jsunit","goog.userAgent","webdriver.testing.Client"], false);
        140goog.addDependency("../webdriver/testing/client.js", ["webdriver.testing.Client"], ["goog.json","goog.net.XmlHttp"], false);
        141goog.addDependency("json/json.js", ["goog.json","goog.json.Replacer","goog.json.Reviver","goog.json.Serializer"], [], false);
        142goog.addDependency("../webdriver/test/testing/testcase_test.js", [], ["goog.Promise","goog.testing.MockControl","goog.testing.PropertyReplacer","goog.testing.mockmatchers","goog.testing.jsunit","goog.testing.recordFunction","goog.userAgent","webdriver.test.testutil","webdriver.testing.TestCase"], false);
        143goog.addDependency("../webdriver/testing/testcase.js", ["webdriver.testing.TestCase"], ["goog.testing.TestCase","webdriver.promise","webdriver.testing.asserts"], false);
        144goog.addDependency("../atoms/json.js", ["bot.json"], ["bot.userAgent","goog.json","goog.userAgent"], false);
        145goog.addDependency("../atoms/userAgent.js", ["bot.userAgent"], ["goog.string","goog.userAgent","goog.userAgent.product","goog.userAgent.product.isVersion"], false);
        146goog.addDependency("useragent/product_isversion.js", ["goog.userAgent.product.isVersion"], ["goog.labs.userAgent.platform","goog.string","goog.userAgent","goog.userAgent.product"], false);
        147goog.addDependency("../webdriver/testing/jsunit.js", ["webdriver.testing.jsunit","webdriver.testing.jsunit.TestRunner"], ["goog.testing.TestRunner","webdriver.testing.Client","webdriver.testing.TestCase"], false);
        148goog.addDependency("../webdriver/testing/window.js", ["webdriver.testing.Window"], ["goog.string","webdriver.promise"], false);
        149goog.addDependency("../webdriver/test/promise_generator_test.js", ["webdriver.test.promise.generator.test"], ["goog.testing.jsunit","webdriver.promise"], false);
        150goog.addDependency("../webdriver/test/until_test.js", ["webdriver.test.until_test"], ["bot.Error","bot.ErrorCode","bot.response","goog.array","goog.string","goog.testing.jsunit","goog.userAgent","webdriver.By","webdriver.CommandName","webdriver.WebDriver","webdriver.WebElement","webdriver.WebElementPromise","webdriver.until"], false);
        151goog.addDependency("../webdriver/test/webdriver_generator_test.js", ["webdriver.test.WebDriver.generator.test"], ["goog.testing.jsunit","webdriver.Session","webdriver.WebDriver"], false);
        \ No newline at end of file diff --git a/docs/source/lib/goog/disposable/disposable.js.src.html b/docs/source/lib/goog/disposable/disposable.js.src.html new file mode 100644 index 0000000..60129c2 --- /dev/null +++ b/docs/source/lib/goog/disposable/disposable.js.src.html @@ -0,0 +1 @@ +disposable.js

        lib/goog/disposable/disposable.js

        1// Copyright 2005 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 Implements the disposable interface. The dispose method is used
        17 * to clean up references and resources.
        18 * @author arv@google.com (Erik Arvidsson)
        19 */
        20
        21
        22goog.provide('goog.Disposable');
        23/** @suppress {extraProvide} */
        24goog.provide('goog.dispose');
        25/** @suppress {extraProvide} */
        26goog.provide('goog.disposeAll');
        27
        28goog.require('goog.disposable.IDisposable');
        29
        30
        31
        32/**
        33 * Class that provides the basic implementation for disposable objects. If your
        34 * class holds one or more references to COM objects, DOM nodes, or other
        35 * disposable objects, it should extend this class or implement the disposable
        36 * interface (defined in goog.disposable.IDisposable).
        37 * @constructor
        38 * @implements {goog.disposable.IDisposable}
        39 */
        40goog.Disposable = function() {
        41 if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
        42 if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {
        43 this.creationStack = new Error().stack;
        44 }
        45 goog.Disposable.instances_[goog.getUid(this)] = this;
        46 }
        47 // Support sealing
        48 this.disposed_ = this.disposed_;
        49 this.onDisposeCallbacks_ = this.onDisposeCallbacks_;
        50};
        51
        52
        53/**
        54 * @enum {number} Different monitoring modes for Disposable.
        55 */
        56goog.Disposable.MonitoringMode = {
        57 /**
        58 * No monitoring.
        59 */
        60 OFF: 0,
        61 /**
        62 * Creating and disposing the goog.Disposable instances is monitored. All
        63 * disposable objects need to call the {@code goog.Disposable} base
        64 * constructor. The PERMANENT mode must be switched on before creating any
        65 * goog.Disposable instances.
        66 */
        67 PERMANENT: 1,
        68 /**
        69 * INTERACTIVE mode can be switched on and off on the fly without producing
        70 * errors. It also doesn't warn if the disposable objects don't call the
        71 * {@code goog.Disposable} base constructor.
        72 */
        73 INTERACTIVE: 2
        74};
        75
        76
        77/**
        78 * @define {number} The monitoring mode of the goog.Disposable
        79 * instances. Default is OFF. Switching on the monitoring is only
        80 * recommended for debugging because it has a significant impact on
        81 * performance and memory usage. If switched off, the monitoring code
        82 * compiles down to 0 bytes.
        83 */
        84goog.define('goog.Disposable.MONITORING_MODE', 0);
        85
        86
        87/**
        88 * @define {boolean} Whether to attach creation stack to each created disposable
        89 * instance; This is only relevant for when MonitoringMode != OFF.
        90 */
        91goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);
        92
        93
        94/**
        95 * Maps the unique ID of every undisposed {@code goog.Disposable} object to
        96 * the object itself.
        97 * @type {!Object<number, !goog.Disposable>}
        98 * @private
        99 */
        100goog.Disposable.instances_ = {};
        101
        102
        103/**
        104 * @return {!Array<!goog.Disposable>} All {@code goog.Disposable} objects that
        105 * haven't been disposed of.
        106 */
        107goog.Disposable.getUndisposedObjects = function() {
        108 var ret = [];
        109 for (var id in goog.Disposable.instances_) {
        110 if (goog.Disposable.instances_.hasOwnProperty(id)) {
        111 ret.push(goog.Disposable.instances_[Number(id)]);
        112 }
        113 }
        114 return ret;
        115};
        116
        117
        118/**
        119 * Clears the registry of undisposed objects but doesn't dispose of them.
        120 */
        121goog.Disposable.clearUndisposedObjects = function() {
        122 goog.Disposable.instances_ = {};
        123};
        124
        125
        126/**
        127 * Whether the object has been disposed of.
        128 * @type {boolean}
        129 * @private
        130 */
        131goog.Disposable.prototype.disposed_ = false;
        132
        133
        134/**
        135 * Callbacks to invoke when this object is disposed.
        136 * @type {Array<!Function>}
        137 * @private
        138 */
        139goog.Disposable.prototype.onDisposeCallbacks_;
        140
        141
        142/**
        143 * If monitoring the goog.Disposable instances is enabled, stores the creation
        144 * stack trace of the Disposable instance.
        145 * @const {string}
        146 */
        147goog.Disposable.prototype.creationStack;
        148
        149
        150/**
        151 * @return {boolean} Whether the object has been disposed of.
        152 * @override
        153 */
        154goog.Disposable.prototype.isDisposed = function() {
        155 return this.disposed_;
        156};
        157
        158
        159/**
        160 * @return {boolean} Whether the object has been disposed of.
        161 * @deprecated Use {@link #isDisposed} instead.
        162 */
        163goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;
        164
        165
        166/**
        167 * Disposes of the object. If the object hasn't already been disposed of, calls
        168 * {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should
        169 * override {@link #disposeInternal} in order to delete references to COM
        170 * objects, DOM nodes, and other disposable objects. Reentrant.
        171 *
        172 * @return {void} Nothing.
        173 * @override
        174 */
        175goog.Disposable.prototype.dispose = function() {
        176 if (!this.disposed_) {
        177 // Set disposed_ to true first, in case during the chain of disposal this
        178 // gets disposed recursively.
        179 this.disposed_ = true;
        180 this.disposeInternal();
        181 if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
        182 var uid = goog.getUid(this);
        183 if (goog.Disposable.MONITORING_MODE ==
        184 goog.Disposable.MonitoringMode.PERMANENT &&
        185 !goog.Disposable.instances_.hasOwnProperty(uid)) {
        186 throw Error(this + ' did not call the goog.Disposable base ' +
        187 'constructor or was disposed of after a clearUndisposedObjects ' +
        188 'call');
        189 }
        190 delete goog.Disposable.instances_[uid];
        191 }
        192 }
        193};
        194
        195
        196/**
        197 * Associates a disposable object with this object so that they will be disposed
        198 * together.
        199 * @param {goog.disposable.IDisposable} disposable that will be disposed when
        200 * this object is disposed.
        201 */
        202goog.Disposable.prototype.registerDisposable = function(disposable) {
        203 this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));
        204};
        205
        206
        207/**
        208 * Invokes a callback function when this object is disposed. Callbacks are
        209 * invoked in the order in which they were added. If a callback is added to
        210 * an already disposed Disposable, it will be called immediately.
        211 * @param {function(this:T):?} callback The callback function.
        212 * @param {T=} opt_scope An optional scope to call the callback in.
        213 * @template T
        214 */
        215goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
        216 if (this.disposed_) {
        217 callback.call(opt_scope);
        218 return;
        219 }
        220 if (!this.onDisposeCallbacks_) {
        221 this.onDisposeCallbacks_ = [];
        222 }
        223
        224 this.onDisposeCallbacks_.push(
        225 goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback);
        226};
        227
        228
        229/**
        230 * Deletes or nulls out any references to COM objects, DOM nodes, or other
        231 * disposable objects. Classes that extend {@code goog.Disposable} should
        232 * override this method.
        233 * Not reentrant. To avoid calling it twice, it must only be called from the
        234 * subclass' {@code disposeInternal} method. Everywhere else the public
        235 * {@code dispose} method must be used.
        236 * For example:
        237 * <pre>
        238 * mypackage.MyClass = function() {
        239 * mypackage.MyClass.base(this, 'constructor');
        240 * // Constructor logic specific to MyClass.
        241 * ...
        242 * };
        243 * goog.inherits(mypackage.MyClass, goog.Disposable);
        244 *
        245 * mypackage.MyClass.prototype.disposeInternal = function() {
        246 * // Dispose logic specific to MyClass.
        247 * ...
        248 * // Call superclass's disposeInternal at the end of the subclass's, like
        249 * // in C++, to avoid hard-to-catch issues.
        250 * mypackage.MyClass.base(this, 'disposeInternal');
        251 * };
        252 * </pre>
        253 * @protected
        254 */
        255goog.Disposable.prototype.disposeInternal = function() {
        256 if (this.onDisposeCallbacks_) {
        257 while (this.onDisposeCallbacks_.length) {
        258 this.onDisposeCallbacks_.shift()();
        259 }
        260 }
        261};
        262
        263
        264/**
        265 * Returns True if we can verify the object is disposed.
        266 * Calls {@code isDisposed} on the argument if it supports it. If obj
        267 * is not an object with an isDisposed() method, return false.
        268 * @param {*} obj The object to investigate.
        269 * @return {boolean} True if we can verify the object is disposed.
        270 */
        271goog.Disposable.isDisposed = function(obj) {
        272 if (obj && typeof obj.isDisposed == 'function') {
        273 return obj.isDisposed();
        274 }
        275 return false;
        276};
        277
        278
        279/**
        280 * Calls {@code dispose} on the argument if it supports it. If obj is not an
        281 * object with a dispose() method, this is a no-op.
        282 * @param {*} obj The object to dispose of.
        283 */
        284goog.dispose = function(obj) {
        285 if (obj && typeof obj.dispose == 'function') {
        286 obj.dispose();
        287 }
        288};
        289
        290
        291/**
        292 * Calls {@code dispose} on each member of the list that supports it. (If the
        293 * member is an ArrayLike, then {@code goog.disposeAll()} will be called
        294 * recursively on each of its members.) If the member is not an object with a
        295 * {@code dispose()} method, then it is ignored.
        296 * @param {...*} var_args The list.
        297 */
        298goog.disposeAll = function(var_args) {
        299 for (var i = 0, len = arguments.length; i < len; ++i) {
        300 var disposable = arguments[i];
        301 if (goog.isArrayLike(disposable)) {
        302 goog.disposeAll.apply(null, disposable);
        303 } else {
        304 goog.dispose(disposable);
        305 }
        306 }
        307};
        \ No newline at end of file diff --git a/docs/source/lib/goog/disposable/idisposable.js.src.html b/docs/source/lib/goog/disposable/idisposable.js.src.html new file mode 100644 index 0000000..a3d4819 --- /dev/null +++ b/docs/source/lib/goog/disposable/idisposable.js.src.html @@ -0,0 +1 @@ +idisposable.js

        lib/goog/disposable/idisposable.js

        1// Copyright 2011 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 the disposable interface. A disposable object
        17 * has a dispose method to to clean up references and resources.
        18 * @author nnaze@google.com (Nathan Naze)
        19 */
        20
        21
        22goog.provide('goog.disposable.IDisposable');
        23
        24
        25
        26/**
        27 * Interface for a disposable object. If a instance requires cleanup
        28 * (references COM objects, DOM notes, or other disposable objects), it should
        29 * implement this interface (it may subclass goog.Disposable).
        30 * @interface
        31 */
        32goog.disposable.IDisposable = function() {};
        33
        34
        35/**
        36 * Disposes of the object and its resources.
        37 * @return {void} Nothing.
        38 */
        39goog.disposable.IDisposable.prototype.dispose = goog.abstractMethod;
        40
        41
        42/**
        43 * @return {boolean} Whether the object has been disposed of.
        44 */
        45goog.disposable.IDisposable.prototype.isDisposed = goog.abstractMethod;
        \ No newline at end of file diff --git a/docs/source/lib/goog/dom/browserfeature.js.src.html b/docs/source/lib/goog/dom/browserfeature.js.src.html new file mode 100644 index 0000000..0c53d6b --- /dev/null +++ b/docs/source/lib/goog/dom/browserfeature.js.src.html @@ -0,0 +1 @@ +browserfeature.js

        lib/goog/dom/browserfeature.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 Browser capability checks for the dom package.
        17 *
        18 */
        19
        20
        21goog.provide('goog.dom.BrowserFeature');
        22
        23goog.require('goog.userAgent');
        24
        25
        26/**
        27 * Enum of browser capabilities.
        28 * @enum {boolean}
        29 */
        30goog.dom.BrowserFeature = {
        31 /**
        32 * Whether attributes 'name' and 'type' can be added to an element after it's
        33 * created. False in Internet Explorer prior to version 9.
        34 */
        35 CAN_ADD_NAME_OR_TYPE_ATTRIBUTES: !goog.userAgent.IE ||
        36 goog.userAgent.isDocumentModeOrHigher(9),
        37
        38 /**
        39 * Whether we can use element.children to access an element's Element
        40 * children. Available since Gecko 1.9.1, IE 9. (IE<9 also includes comment
        41 * nodes in the collection.)
        42 */
        43 CAN_USE_CHILDREN_ATTRIBUTE: !goog.userAgent.GECKO && !goog.userAgent.IE ||
        44 goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9) ||
        45 goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1'),
        46
        47 /**
        48 * Opera, Safari 3, and Internet Explorer 9 all support innerText but they
        49 * include text nodes in script and style tags. Not document-mode-dependent.
        50 */
        51 CAN_USE_INNER_TEXT: (
        52 goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')),
        53
        54 /**
        55 * MSIE, Opera, and Safari>=4 support element.parentElement to access an
        56 * element's parent if it is an Element.
        57 */
        58 CAN_USE_PARENT_ELEMENT_PROPERTY: goog.userAgent.IE || goog.userAgent.OPERA ||
        59 goog.userAgent.WEBKIT,
        60
        61 /**
        62 * Whether NoScope elements need a scoped element written before them in
        63 * innerHTML.
        64 * MSDN: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx#1
        65 */
        66 INNER_HTML_NEEDS_SCOPED_ELEMENT: goog.userAgent.IE,
        67
        68 /**
        69 * Whether we use legacy IE range API.
        70 */
        71 LEGACY_IE_RANGES: goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)
        72};
        \ No newline at end of file diff --git a/docs/source/lib/goog/dom/dom.js.src.html b/docs/source/lib/goog/dom/dom.js.src.html new file mode 100644 index 0000000..626f007 --- /dev/null +++ b/docs/source/lib/goog/dom/dom.js.src.html @@ -0,0 +1 @@ +dom.js

        lib/goog/dom/dom.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 the browser's Document Object Model
        17 * Inspiration taken *heavily* from mochikit (http://mochikit.com/).
        18 *
        19 * You can use {@link goog.dom.DomHelper} to create new dom helpers that refer
        20 * to a different document object. This is useful if you are working with
        21 * frames or multiple windows.
        22 *
        23 * @author arv@google.com (Erik Arvidsson)
        24 */
        25
        26
        27// TODO(arv): Rename/refactor getTextContent and getRawTextContent. The problem
        28// is that getTextContent should mimic the DOM3 textContent. We should add a
        29// getInnerText (or getText) which tries to return the visible text, innerText.
        30
        31
        32goog.provide('goog.dom');
        33goog.provide('goog.dom.Appendable');
        34goog.provide('goog.dom.DomHelper');
        35
        36goog.require('goog.array');
        37goog.require('goog.asserts');
        38goog.require('goog.dom.BrowserFeature');
        39goog.require('goog.dom.NodeType');
        40goog.require('goog.dom.TagName');
        41goog.require('goog.dom.safe');
        42goog.require('goog.html.SafeHtml');
        43goog.require('goog.math.Coordinate');
        44goog.require('goog.math.Size');
        45goog.require('goog.object');
        46goog.require('goog.string');
        47goog.require('goog.string.Unicode');
        48goog.require('goog.userAgent');
        49
        50
        51/**
        52 * @define {boolean} Whether we know at compile time that the browser is in
        53 * quirks mode.
        54 */
        55goog.define('goog.dom.ASSUME_QUIRKS_MODE', false);
        56
        57
        58/**
        59 * @define {boolean} Whether we know at compile time that the browser is in
        60 * standards compliance mode.
        61 */
        62goog.define('goog.dom.ASSUME_STANDARDS_MODE', false);
        63
        64
        65/**
        66 * Whether we know the compatibility mode at compile time.
        67 * @type {boolean}
        68 * @private
        69 */
        70goog.dom.COMPAT_MODE_KNOWN_ =
        71 goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;
        72
        73
        74/**
        75 * Gets the DomHelper object for the document where the element resides.
        76 * @param {(Node|Window)=} opt_element If present, gets the DomHelper for this
        77 * element.
        78 * @return {!goog.dom.DomHelper} The DomHelper.
        79 */
        80goog.dom.getDomHelper = function(opt_element) {
        81 return opt_element ?
        82 new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element)) :
        83 (goog.dom.defaultDomHelper_ ||
        84 (goog.dom.defaultDomHelper_ = new goog.dom.DomHelper()));
        85};
        86
        87
        88/**
        89 * Cached default DOM helper.
        90 * @type {goog.dom.DomHelper}
        91 * @private
        92 */
        93goog.dom.defaultDomHelper_;
        94
        95
        96/**
        97 * Gets the document object being used by the dom library.
        98 * @return {!Document} Document object.
        99 */
        100goog.dom.getDocument = function() {
        101 return document;
        102};
        103
        104
        105/**
        106 * Gets an element from the current document by element id.
        107 *
        108 * If an Element is passed in, it is returned.
        109 *
        110 * @param {string|Element} element Element ID or a DOM node.
        111 * @return {Element} The element with the given ID, or the node passed in.
        112 */
        113goog.dom.getElement = function(element) {
        114 return goog.dom.getElementHelper_(document, element);
        115};
        116
        117
        118/**
        119 * Gets an element by id from the given document (if present).
        120 * If an element is given, it is returned.
        121 * @param {!Document} doc
        122 * @param {string|Element} element Element ID or a DOM node.
        123 * @return {Element} The resulting element.
        124 * @private
        125 */
        126goog.dom.getElementHelper_ = function(doc, element) {
        127 return goog.isString(element) ?
        128 doc.getElementById(element) :
        129 element;
        130};
        131
        132
        133/**
        134 * Gets an element by id, asserting that the element is found.
        135 *
        136 * This is used when an element is expected to exist, and should fail with
        137 * an assertion error if it does not (if assertions are enabled).
        138 *
        139 * @param {string} id Element ID.
        140 * @return {!Element} The element with the given ID, if it exists.
        141 */
        142goog.dom.getRequiredElement = function(id) {
        143 return goog.dom.getRequiredElementHelper_(document, id);
        144};
        145
        146
        147/**
        148 * Helper function for getRequiredElementHelper functions, both static and
        149 * on DomHelper. Asserts the element with the given id exists.
        150 * @param {!Document} doc
        151 * @param {string} id
        152 * @return {!Element} The element with the given ID, if it exists.
        153 * @private
        154 */
        155goog.dom.getRequiredElementHelper_ = function(doc, id) {
        156 // To prevent users passing in Elements as is permitted in getElement().
        157 goog.asserts.assertString(id);
        158 var element = goog.dom.getElementHelper_(doc, id);
        159 element = goog.asserts.assertElement(element,
        160 'No element found with id: ' + id);
        161 return element;
        162};
        163
        164
        165/**
        166 * Alias for getElement.
        167 * @param {string|Element} element Element ID or a DOM node.
        168 * @return {Element} The element with the given ID, or the node passed in.
        169 * @deprecated Use {@link goog.dom.getElement} instead.
        170 */
        171goog.dom.$ = goog.dom.getElement;
        172
        173
        174/**
        175 * Looks up elements by both tag and class name, using browser native functions
        176 * ({@code querySelectorAll}, {@code getElementsByTagName} or
        177 * {@code getElementsByClassName}) where possible. This function
        178 * is a useful, if limited, way of collecting a list of DOM elements
        179 * with certain characteristics. {@code goog.dom.query} offers a
        180 * more powerful and general solution which allows matching on CSS3
        181 * selector expressions, but at increased cost in code size. If all you
        182 * need is particular tags belonging to a single class, this function
        183 * is fast and sleek.
        184 *
        185 * Note that tag names are case sensitive in the SVG namespace, and this
        186 * function converts opt_tag to uppercase for comparisons. For queries in the
        187 * SVG namespace you should use querySelector or querySelectorAll instead.
        188 * https://bugzilla.mozilla.org/show_bug.cgi?id=963870
        189 * https://bugs.webkit.org/show_bug.cgi?id=83438
        190 *
        191 * @see {goog.dom.query}
        192 *
        193 * @param {?string=} opt_tag Element tag name.
        194 * @param {?string=} opt_class Optional class name.
        195 * @param {(Document|Element)=} opt_el Optional element to look in.
        196 * @return { {length: number} } Array-like list of elements (only a length
        197 * property and numerical indices are guaranteed to exist).
        198 */
        199goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
        200 return goog.dom.getElementsByTagNameAndClass_(document, opt_tag, opt_class,
        201 opt_el);
        202};
        203
        204
        205/**
        206 * Returns a static, array-like list of the elements with the provided
        207 * className.
        208 * @see {goog.dom.query}
        209 * @param {string} className the name of the class to look for.
        210 * @param {(Document|Element)=} opt_el Optional element to look in.
        211 * @return { {length: number} } The items found with the class name provided.
        212 */
        213goog.dom.getElementsByClass = function(className, opt_el) {
        214 var parent = opt_el || document;
        215 if (goog.dom.canUseQuerySelector_(parent)) {
        216 return parent.querySelectorAll('.' + className);
        217 }
        218 return goog.dom.getElementsByTagNameAndClass_(
        219 document, '*', className, opt_el);
        220};
        221
        222
        223/**
        224 * Returns the first element with the provided className.
        225 * @see {goog.dom.query}
        226 * @param {string} className the name of the class to look for.
        227 * @param {Element|Document=} opt_el Optional element to look in.
        228 * @return {Element} The first item with the class name provided.
        229 */
        230goog.dom.getElementByClass = function(className, opt_el) {
        231 var parent = opt_el || document;
        232 var retVal = null;
        233 if (parent.getElementsByClassName) {
        234 retVal = parent.getElementsByClassName(className)[0];
        235 } else if (goog.dom.canUseQuerySelector_(parent)) {
        236 retVal = parent.querySelector('.' + className);
        237 } else {
        238 retVal = goog.dom.getElementsByTagNameAndClass_(
        239 document, '*', className, opt_el)[0];
        240 }
        241 return retVal || null;
        242};
        243
        244
        245/**
        246 * Ensures an element with the given className exists, and then returns the
        247 * first element with the provided className.
        248 * @see {goog.dom.query}
        249 * @param {string} className the name of the class to look for.
        250 * @param {!Element|!Document=} opt_root Optional element or document to look
        251 * in.
        252 * @return {!Element} The first item with the class name provided.
        253 * @throws {goog.asserts.AssertionError} Thrown if no element is found.
        254 */
        255goog.dom.getRequiredElementByClass = function(className, opt_root) {
        256 var retValue = goog.dom.getElementByClass(className, opt_root);
        257 return goog.asserts.assert(retValue,
        258 'No element found with className: ' + className);
        259};
        260
        261
        262/**
        263 * Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and
        264 * fast W3C Selectors API.
        265 * @param {!(Element|Document)} parent The parent document object.
        266 * @return {boolean} whether or not we can use parent.querySelector* APIs.
        267 * @private
        268 */
        269goog.dom.canUseQuerySelector_ = function(parent) {
        270 return !!(parent.querySelectorAll && parent.querySelector);
        271};
        272
        273
        274/**
        275 * Helper for {@code getElementsByTagNameAndClass}.
        276 * @param {!Document} doc The document to get the elements in.
        277 * @param {?string=} opt_tag Element tag name.
        278 * @param {?string=} opt_class Optional class name.
        279 * @param {(Document|Element)=} opt_el Optional element to look in.
        280 * @return { {length: number} } Array-like list of elements (only a length
        281 * property and numerical indices are guaranteed to exist).
        282 * @private
        283 */
        284goog.dom.getElementsByTagNameAndClass_ = function(doc, opt_tag, opt_class,
        285 opt_el) {
        286 var parent = opt_el || doc;
        287 var tagName = (opt_tag && opt_tag != '*') ? opt_tag.toUpperCase() : '';
        288
        289 if (goog.dom.canUseQuerySelector_(parent) &&
        290 (tagName || opt_class)) {
        291 var query = tagName + (opt_class ? '.' + opt_class : '');
        292 return parent.querySelectorAll(query);
        293 }
        294
        295 // Use the native getElementsByClassName if available, under the assumption
        296 // that even when the tag name is specified, there will be fewer elements to
        297 // filter through when going by class than by tag name
        298 if (opt_class && parent.getElementsByClassName) {
        299 var els = parent.getElementsByClassName(opt_class);
        300
        301 if (tagName) {
        302 var arrayLike = {};
        303 var len = 0;
        304
        305 // Filter for specific tags if requested.
        306 for (var i = 0, el; el = els[i]; i++) {
        307 if (tagName == el.nodeName) {
        308 arrayLike[len++] = el;
        309 }
        310 }
        311 arrayLike.length = len;
        312
        313 return arrayLike;
        314 } else {
        315 return els;
        316 }
        317 }
        318
        319 var els = parent.getElementsByTagName(tagName || '*');
        320
        321 if (opt_class) {
        322 var arrayLike = {};
        323 var len = 0;
        324 for (var i = 0, el; el = els[i]; i++) {
        325 var className = el.className;
        326 // Check if className has a split function since SVG className does not.
        327 if (typeof className.split == 'function' &&
        328 goog.array.contains(className.split(/\s+/), opt_class)) {
        329 arrayLike[len++] = el;
        330 }
        331 }
        332 arrayLike.length = len;
        333 return arrayLike;
        334 } else {
        335 return els;
        336 }
        337};
        338
        339
        340/**
        341 * Alias for {@code getElementsByTagNameAndClass}.
        342 * @param {?string=} opt_tag Element tag name.
        343 * @param {?string=} opt_class Optional class name.
        344 * @param {Element=} opt_el Optional element to look in.
        345 * @return { {length: number} } Array-like list of elements (only a length
        346 * property and numerical indices are guaranteed to exist).
        347 * @deprecated Use {@link goog.dom.getElementsByTagNameAndClass} instead.
        348 */
        349goog.dom.$$ = goog.dom.getElementsByTagNameAndClass;
        350
        351
        352/**
        353 * Sets multiple properties on a node.
        354 * @param {Element} element DOM node to set properties on.
        355 * @param {Object} properties Hash of property:value pairs.
        356 */
        357goog.dom.setProperties = function(element, properties) {
        358 goog.object.forEach(properties, function(val, key) {
        359 if (key == 'style') {
        360 element.style.cssText = val;
        361 } else if (key == 'class') {
        362 element.className = val;
        363 } else if (key == 'for') {
        364 element.htmlFor = val;
        365 } else if (goog.dom.DIRECT_ATTRIBUTE_MAP_.hasOwnProperty(key)) {
        366 element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val);
        367 } else if (goog.string.startsWith(key, 'aria-') ||
        368 goog.string.startsWith(key, 'data-')) {
        369 element.setAttribute(key, val);
        370 } else {
        371 element[key] = val;
        372 }
        373 });
        374};
        375
        376
        377/**
        378 * Map of attributes that should be set using
        379 * element.setAttribute(key, val) instead of element[key] = val. Used
        380 * by goog.dom.setProperties.
        381 *
        382 * @private {!Object<string, string>}
        383 * @const
        384 */
        385goog.dom.DIRECT_ATTRIBUTE_MAP_ = {
        386 'cellpadding': 'cellPadding',
        387 'cellspacing': 'cellSpacing',
        388 'colspan': 'colSpan',
        389 'frameborder': 'frameBorder',
        390 'height': 'height',
        391 'maxlength': 'maxLength',
        392 'role': 'role',
        393 'rowspan': 'rowSpan',
        394 'type': 'type',
        395 'usemap': 'useMap',
        396 'valign': 'vAlign',
        397 'width': 'width'
        398};
        399
        400
        401/**
        402 * Gets the dimensions of the viewport.
        403 *
        404 * Gecko Standards mode:
        405 * docEl.clientWidth Width of viewport excluding scrollbar.
        406 * win.innerWidth Width of viewport including scrollbar.
        407 * body.clientWidth Width of body element.
        408 *
        409 * docEl.clientHeight Height of viewport excluding scrollbar.
        410 * win.innerHeight Height of viewport including scrollbar.
        411 * body.clientHeight Height of document.
        412 *
        413 * Gecko Backwards compatible mode:
        414 * docEl.clientWidth Width of viewport excluding scrollbar.
        415 * win.innerWidth Width of viewport including scrollbar.
        416 * body.clientWidth Width of viewport excluding scrollbar.
        417 *
        418 * docEl.clientHeight Height of document.
        419 * win.innerHeight Height of viewport including scrollbar.
        420 * body.clientHeight Height of viewport excluding scrollbar.
        421 *
        422 * IE6/7 Standards mode:
        423 * docEl.clientWidth Width of viewport excluding scrollbar.
        424 * win.innerWidth Undefined.
        425 * body.clientWidth Width of body element.
        426 *
        427 * docEl.clientHeight Height of viewport excluding scrollbar.
        428 * win.innerHeight Undefined.
        429 * body.clientHeight Height of document element.
        430 *
        431 * IE5 + IE6/7 Backwards compatible mode:
        432 * docEl.clientWidth 0.
        433 * win.innerWidth Undefined.
        434 * body.clientWidth Width of viewport excluding scrollbar.
        435 *
        436 * docEl.clientHeight 0.
        437 * win.innerHeight Undefined.
        438 * body.clientHeight Height of viewport excluding scrollbar.
        439 *
        440 * Opera 9 Standards and backwards compatible mode:
        441 * docEl.clientWidth Width of viewport excluding scrollbar.
        442 * win.innerWidth Width of viewport including scrollbar.
        443 * body.clientWidth Width of viewport excluding scrollbar.
        444 *
        445 * docEl.clientHeight Height of document.
        446 * win.innerHeight Height of viewport including scrollbar.
        447 * body.clientHeight Height of viewport excluding scrollbar.
        448 *
        449 * WebKit:
        450 * Safari 2
        451 * docEl.clientHeight Same as scrollHeight.
        452 * docEl.clientWidth Same as innerWidth.
        453 * win.innerWidth Width of viewport excluding scrollbar.
        454 * win.innerHeight Height of the viewport including scrollbar.
        455 * frame.innerHeight Height of the viewport exluding scrollbar.
        456 *
        457 * Safari 3 (tested in 522)
        458 *
        459 * docEl.clientWidth Width of viewport excluding scrollbar.
        460 * docEl.clientHeight Height of viewport excluding scrollbar in strict mode.
        461 * body.clientHeight Height of viewport excluding scrollbar in quirks mode.
        462 *
        463 * @param {Window=} opt_window Optional window element to test.
        464 * @return {!goog.math.Size} Object with values 'width' and 'height'.
        465 */
        466goog.dom.getViewportSize = function(opt_window) {
        467 // TODO(arv): This should not take an argument
        468 return goog.dom.getViewportSize_(opt_window || window);
        469};
        470
        471
        472/**
        473 * Helper for {@code getViewportSize}.
        474 * @param {Window} win The window to get the view port size for.
        475 * @return {!goog.math.Size} Object with values 'width' and 'height'.
        476 * @private
        477 */
        478goog.dom.getViewportSize_ = function(win) {
        479 var doc = win.document;
        480 var el = goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body;
        481 return new goog.math.Size(el.clientWidth, el.clientHeight);
        482};
        483
        484
        485/**
        486 * Calculates the height of the document.
        487 *
        488 * @return {number} The height of the current document.
        489 */
        490goog.dom.getDocumentHeight = function() {
        491 return goog.dom.getDocumentHeight_(window);
        492};
        493
        494
        495/**
        496 * Calculates the height of the document of the given window.
        497 *
        498 * Function code copied from the opensocial gadget api:
        499 * gadgets.window.adjustHeight(opt_height)
        500 *
        501 * @private
        502 * @param {!Window} win The window whose document height to retrieve.
        503 * @return {number} The height of the document of the given window.
        504 */
        505goog.dom.getDocumentHeight_ = function(win) {
        506 // NOTE(eae): This method will return the window size rather than the document
        507 // size in webkit quirks mode.
        508 var doc = win.document;
        509 var height = 0;
        510
        511 if (doc) {
        512 // Calculating inner content height is hard and different between
        513 // browsers rendering in Strict vs. Quirks mode. We use a combination of
        514 // three properties within document.body and document.documentElement:
        515 // - scrollHeight
        516 // - offsetHeight
        517 // - clientHeight
        518 // These values differ significantly between browsers and rendering modes.
        519 // But there are patterns. It just takes a lot of time and persistence
        520 // to figure out.
        521
        522 var body = doc.body;
        523 var docEl = /** @type {!HTMLElement} */ (doc.documentElement);
        524 if (!(docEl && body)) {
        525 return 0;
        526 }
        527
        528 // Get the height of the viewport
        529 var vh = goog.dom.getViewportSize_(win).height;
        530 if (goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) {
        531 // In Strict mode:
        532 // The inner content height is contained in either:
        533 // document.documentElement.scrollHeight
        534 // document.documentElement.offsetHeight
        535 // Based on studying the values output by different browsers,
        536 // use the value that's NOT equal to the viewport height found above.
        537 height = docEl.scrollHeight != vh ?
        538 docEl.scrollHeight : docEl.offsetHeight;
        539 } else {
        540 // In Quirks mode:
        541 // documentElement.clientHeight is equal to documentElement.offsetHeight
        542 // except in IE. In most browsers, document.documentElement can be used
        543 // to calculate the inner content height.
        544 // However, in other browsers (e.g. IE), document.body must be used
        545 // instead. How do we know which one to use?
        546 // If document.documentElement.clientHeight does NOT equal
        547 // document.documentElement.offsetHeight, then use document.body.
        548 var sh = docEl.scrollHeight;
        549 var oh = docEl.offsetHeight;
        550 if (docEl.clientHeight != oh) {
        551 sh = body.scrollHeight;
        552 oh = body.offsetHeight;
        553 }
        554
        555 // Detect whether the inner content height is bigger or smaller
        556 // than the bounding box (viewport). If bigger, take the larger
        557 // value. If smaller, take the smaller value.
        558 if (sh > vh) {
        559 // Content is larger
        560 height = sh > oh ? sh : oh;
        561 } else {
        562 // Content is smaller
        563 height = sh < oh ? sh : oh;
        564 }
        565 }
        566 }
        567
        568 return height;
        569};
        570
        571
        572/**
        573 * Gets the page scroll distance as a coordinate object.
        574 *
        575 * @param {Window=} opt_window Optional window element to test.
        576 * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
        577 * @deprecated Use {@link goog.dom.getDocumentScroll} instead.
        578 */
        579goog.dom.getPageScroll = function(opt_window) {
        580 var win = opt_window || goog.global || window;
        581 return goog.dom.getDomHelper(win.document).getDocumentScroll();
        582};
        583
        584
        585/**
        586 * Gets the document scroll distance as a coordinate object.
        587 *
        588 * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
        589 */
        590goog.dom.getDocumentScroll = function() {
        591 return goog.dom.getDocumentScroll_(document);
        592};
        593
        594
        595/**
        596 * Helper for {@code getDocumentScroll}.
        597 *
        598 * @param {!Document} doc The document to get the scroll for.
        599 * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
        600 * @private
        601 */
        602goog.dom.getDocumentScroll_ = function(doc) {
        603 var el = goog.dom.getDocumentScrollElement_(doc);
        604 var win = goog.dom.getWindow_(doc);
        605 if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') &&
        606 win.pageYOffset != el.scrollTop) {
        607 // The keyboard on IE10 touch devices shifts the page using the pageYOffset
        608 // without modifying scrollTop. For this case, we want the body scroll
        609 // offsets.
        610 return new goog.math.Coordinate(el.scrollLeft, el.scrollTop);
        611 }
        612 return new goog.math.Coordinate(win.pageXOffset || el.scrollLeft,
        613 win.pageYOffset || el.scrollTop);
        614};
        615
        616
        617/**
        618 * Gets the document scroll element.
        619 * @return {!Element} Scrolling element.
        620 */
        621goog.dom.getDocumentScrollElement = function() {
        622 return goog.dom.getDocumentScrollElement_(document);
        623};
        624
        625
        626/**
        627 * Helper for {@code getDocumentScrollElement}.
        628 * @param {!Document} doc The document to get the scroll element for.
        629 * @return {!Element} Scrolling element.
        630 * @private
        631 */
        632goog.dom.getDocumentScrollElement_ = function(doc) {
        633 // Old WebKit needs body.scrollLeft in both quirks mode and strict mode. We
        634 // also default to the documentElement if the document does not have a body
        635 // (e.g. a SVG document).
        636 // Uses http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement to
        637 // avoid trying to guess about browser behavior from the UA string.
        638 if (doc.scrollingElement) {
        639 return doc.scrollingElement;
        640 }
        641 if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) {
        642 return doc.documentElement;
        643 }
        644 return doc.body || doc.documentElement;
        645};
        646
        647
        648/**
        649 * Gets the window object associated with the given document.
        650 *
        651 * @param {Document=} opt_doc Document object to get window for.
        652 * @return {!Window} The window associated with the given document.
        653 */
        654goog.dom.getWindow = function(opt_doc) {
        655 // TODO(arv): This should not take an argument.
        656 return opt_doc ? goog.dom.getWindow_(opt_doc) : window;
        657};
        658
        659
        660/**
        661 * Helper for {@code getWindow}.
        662 *
        663 * @param {!Document} doc Document object to get window for.
        664 * @return {!Window} The window associated with the given document.
        665 * @private
        666 */
        667goog.dom.getWindow_ = function(doc) {
        668 return doc.parentWindow || doc.defaultView;
        669};
        670
        671
        672/**
        673 * Returns a dom node with a set of attributes. This function accepts varargs
        674 * for subsequent nodes to be added. Subsequent nodes will be added to the
        675 * first node as childNodes.
        676 *
        677 * So:
        678 * <code>createDom('div', null, createDom('p'), createDom('p'));</code>
        679 * would return a div with two child paragraphs
        680 *
        681 * @param {string} tagName Tag to create.
        682 * @param {(Object|Array<string>|string)=} opt_attributes If object, then a map
        683 * of name-value pairs for attributes. If a string, then this is the
        684 * className of the new element. If an array, the elements will be joined
        685 * together as the className of the new element.
        686 * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or
        687 * strings for text nodes. If one of the var_args is an array or NodeList,
        688 * its elements will be added as childNodes instead.
        689 * @return {!Element} Reference to a DOM node.
        690 */
        691goog.dom.createDom = function(tagName, opt_attributes, var_args) {
        692 return goog.dom.createDom_(document, arguments);
        693};
        694
        695
        696/**
        697 * Helper for {@code createDom}.
        698 * @param {!Document} doc The document to create the DOM in.
        699 * @param {!Arguments} args Argument object passed from the callers. See
        700 * {@code goog.dom.createDom} for details.
        701 * @return {!Element} Reference to a DOM node.
        702 * @private
        703 */
        704goog.dom.createDom_ = function(doc, args) {
        705 var tagName = args[0];
        706 var attributes = args[1];
        707
        708 // Internet Explorer is dumb:
        709 // name: https://msdn.microsoft.com/en-us/library/ms534184(v=vs.85).aspx
        710 // type: https://msdn.microsoft.com/en-us/library/ms534700(v=vs.85).aspx
        711 // Also does not allow setting of 'type' attribute on 'input' or 'button'.
        712 if (!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES && attributes &&
        713 (attributes.name || attributes.type)) {
        714 var tagNameArr = ['<', tagName];
        715 if (attributes.name) {
        716 tagNameArr.push(' name="', goog.string.htmlEscape(attributes.name), '"');
        717 }
        718 if (attributes.type) {
        719 tagNameArr.push(' type="', goog.string.htmlEscape(attributes.type), '"');
        720
        721 // Clone attributes map to remove 'type' without mutating the input.
        722 var clone = {};
        723 goog.object.extend(clone, attributes);
        724
        725 // JSCompiler can't see how goog.object.extend added this property,
        726 // because it was essentially added by reflection.
        727 // So it needs to be quoted.
        728 delete clone['type'];
        729
        730 attributes = clone;
        731 }
        732 tagNameArr.push('>');
        733 tagName = tagNameArr.join('');
        734 }
        735
        736 var element = doc.createElement(tagName);
        737
        738 if (attributes) {
        739 if (goog.isString(attributes)) {
        740 element.className = attributes;
        741 } else if (goog.isArray(attributes)) {
        742 element.className = attributes.join(' ');
        743 } else {
        744 goog.dom.setProperties(element, attributes);
        745 }
        746 }
        747
        748 if (args.length > 2) {
        749 goog.dom.append_(doc, element, args, 2);
        750 }
        751
        752 return element;
        753};
        754
        755
        756/**
        757 * Appends a node with text or other nodes.
        758 * @param {!Document} doc The document to create new nodes in.
        759 * @param {!Node} parent The node to append nodes to.
        760 * @param {!Arguments} args The values to add. See {@code goog.dom.append}.
        761 * @param {number} startIndex The index of the array to start from.
        762 * @private
        763 */
        764goog.dom.append_ = function(doc, parent, args, startIndex) {
        765 function childHandler(child) {
        766 // TODO(user): More coercion, ala MochiKit?
        767 if (child) {
        768 parent.appendChild(goog.isString(child) ?
        769 doc.createTextNode(child) : child);
        770 }
        771 }
        772
        773 for (var i = startIndex; i < args.length; i++) {
        774 var arg = args[i];
        775 // TODO(attila): Fix isArrayLike to return false for a text node.
        776 if (goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {
        777 // If the argument is a node list, not a real array, use a clone,
        778 // because forEach can't be used to mutate a NodeList.
        779 goog.array.forEach(goog.dom.isNodeList(arg) ?
        780 goog.array.toArray(arg) : arg,
        781 childHandler);
        782 } else {
        783 childHandler(arg);
        784 }
        785 }
        786};
        787
        788
        789/**
        790 * Alias for {@code createDom}.
        791 * @param {string} tagName Tag to create.
        792 * @param {(string|Object)=} opt_attributes If object, then a map of name-value
        793 * pairs for attributes. If a string, then this is the className of the new
        794 * element.
        795 * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or
        796 * strings for text nodes. If one of the var_args is an array, its
        797 * children will be added as childNodes instead.
        798 * @return {!Element} Reference to a DOM node.
        799 * @deprecated Use {@link goog.dom.createDom} instead.
        800 */
        801goog.dom.$dom = goog.dom.createDom;
        802
        803
        804/**
        805 * Creates a new element.
        806 * @param {string} name Tag name.
        807 * @return {!Element} The new element.
        808 */
        809goog.dom.createElement = function(name) {
        810 return document.createElement(name);
        811};
        812
        813
        814/**
        815 * Creates a new text node.
        816 * @param {number|string} content Content.
        817 * @return {!Text} The new text node.
        818 */
        819goog.dom.createTextNode = function(content) {
        820 return document.createTextNode(String(content));
        821};
        822
        823
        824/**
        825 * Create a table.
        826 * @param {number} rows The number of rows in the table. Must be >= 1.
        827 * @param {number} columns The number of columns in the table. Must be >= 1.
        828 * @param {boolean=} opt_fillWithNbsp If true, fills table entries with
        829 * {@code goog.string.Unicode.NBSP} characters.
        830 * @return {!Element} The created table.
        831 */
        832goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {
        833 // TODO(user): Return HTMLTableElement, also in prototype function.
        834 // Callers need to be updated to e.g. not assign numbers to table.cellSpacing.
        835 return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp);
        836};
        837
        838
        839/**
        840 * Create a table.
        841 * @param {!Document} doc Document object to use to create the table.
        842 * @param {number} rows The number of rows in the table. Must be >= 1.
        843 * @param {number} columns The number of columns in the table. Must be >= 1.
        844 * @param {boolean} fillWithNbsp If true, fills table entries with
        845 * {@code goog.string.Unicode.NBSP} characters.
        846 * @return {!HTMLTableElement} The created table.
        847 * @private
        848 */
        849goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {
        850 var table = /** @type {!HTMLTableElement} */
        851 (doc.createElement(goog.dom.TagName.TABLE));
        852 var tbody = table.appendChild(doc.createElement(goog.dom.TagName.TBODY));
        853 for (var i = 0; i < rows; i++) {
        854 var tr = doc.createElement(goog.dom.TagName.TR);
        855 for (var j = 0; j < columns; j++) {
        856 var td = doc.createElement(goog.dom.TagName.TD);
        857 // IE <= 9 will create a text node if we set text content to the empty
        858 // string, so we avoid doing it unless necessary. This ensures that the
        859 // same DOM tree is returned on all browsers.
        860 if (fillWithNbsp) {
        861 goog.dom.setTextContent(td, goog.string.Unicode.NBSP);
        862 }
        863 tr.appendChild(td);
        864 }
        865 tbody.appendChild(tr);
        866 }
        867 return table;
        868};
        869
        870
        871/**
        872 * Converts HTML markup into a node.
        873 * @param {!goog.html.SafeHtml} html The HTML markup to convert.
        874 * @return {!Node} The resulting node.
        875 */
        876goog.dom.safeHtmlToNode = function(html) {
        877 return goog.dom.safeHtmlToNode_(document, html);
        878};
        879
        880
        881/**
        882 * Helper for {@code safeHtmlToNode}.
        883 * @param {!Document} doc The document.
        884 * @param {!goog.html.SafeHtml} html The HTML markup to convert.
        885 * @return {!Node} The resulting node.
        886 * @private
        887 */
        888goog.dom.safeHtmlToNode_ = function(doc, html) {
        889 var tempDiv = doc.createElement(goog.dom.TagName.DIV);
        890 if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
        891 goog.dom.safe.setInnerHtml(tempDiv,
        892 goog.html.SafeHtml.concat(goog.html.SafeHtml.create('br'), html));
        893 tempDiv.removeChild(tempDiv.firstChild);
        894 } else {
        895 goog.dom.safe.setInnerHtml(tempDiv, html);
        896 }
        897 return goog.dom.childrenToNode_(doc, tempDiv);
        898};
        899
        900
        901/**
        902 * Converts an HTML string into a document fragment. The string must be
        903 * sanitized in order to avoid cross-site scripting. For example
        904 * {@code goog.dom.htmlToDocumentFragment('&lt;img src=x onerror=alert(0)&gt;')}
        905 * triggers an alert in all browsers, even if the returned document fragment
        906 * is thrown away immediately.
        907 *
        908 * NOTE: This method doesn't work if your htmlString contains elements that
        909 * can't be contained in a <div>. For example, <tr>.
        910 *
        911 * @param {string} htmlString The HTML string to convert.
        912 * @return {!Node} The resulting document fragment.
        913 */
        914goog.dom.htmlToDocumentFragment = function(htmlString) {
        915 return goog.dom.htmlToDocumentFragment_(document, htmlString);
        916};
        917
        918
        919// TODO(jakubvrana): Merge with {@code safeHtmlToNode_}.
        920/**
        921 * Helper for {@code htmlToDocumentFragment}.
        922 *
        923 * @param {!Document} doc The document.
        924 * @param {string} htmlString The HTML string to convert.
        925 * @return {!Node} The resulting document fragment.
        926 * @private
        927 */
        928goog.dom.htmlToDocumentFragment_ = function(doc, htmlString) {
        929 var tempDiv = doc.createElement(goog.dom.TagName.DIV);
        930 if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
        931 tempDiv.innerHTML = '<br>' + htmlString;
        932 tempDiv.removeChild(tempDiv.firstChild);
        933 } else {
        934 tempDiv.innerHTML = htmlString;
        935 }
        936 return goog.dom.childrenToNode_(doc, tempDiv);
        937};
        938
        939
        940/**
        941 * Helper for {@code htmlToDocumentFragment_}.
        942 * @param {!Document} doc The document.
        943 * @param {!Node} tempDiv The input node.
        944 * @return {!Node} The resulting node.
        945 * @private
        946 */
        947goog.dom.childrenToNode_ = function(doc, tempDiv) {
        948 if (tempDiv.childNodes.length == 1) {
        949 return tempDiv.removeChild(tempDiv.firstChild);
        950 } else {
        951 var fragment = doc.createDocumentFragment();
        952 while (tempDiv.firstChild) {
        953 fragment.appendChild(tempDiv.firstChild);
        954 }
        955 return fragment;
        956 }
        957};
        958
        959
        960/**
        961 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
        962 * mode, false otherwise.
        963 * @return {boolean} True if in CSS1-compatible mode.
        964 */
        965goog.dom.isCss1CompatMode = function() {
        966 return goog.dom.isCss1CompatMode_(document);
        967};
        968
        969
        970/**
        971 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
        972 * mode, false otherwise.
        973 * @param {!Document} doc The document to check.
        974 * @return {boolean} True if in CSS1-compatible mode.
        975 * @private
        976 */
        977goog.dom.isCss1CompatMode_ = function(doc) {
        978 if (goog.dom.COMPAT_MODE_KNOWN_) {
        979 return goog.dom.ASSUME_STANDARDS_MODE;
        980 }
        981
        982 return doc.compatMode == 'CSS1Compat';
        983};
        984
        985
        986/**
        987 * Determines if the given node can contain children, intended to be used for
        988 * HTML generation.
        989 *
        990 * IE natively supports node.canHaveChildren but has inconsistent behavior.
        991 * Prior to IE8 the base tag allows children and in IE9 all nodes return true
        992 * for canHaveChildren.
        993 *
        994 * In practice all non-IE browsers allow you to add children to any node, but
        995 * the behavior is inconsistent:
        996 *
        997 * <pre>
        998 * var a = document.createElement(goog.dom.TagName.BR);
        999 * a.appendChild(document.createTextNode('foo'));
        1000 * a.appendChild(document.createTextNode('bar'));
        1001 * console.log(a.childNodes.length); // 2
        1002 * console.log(a.innerHTML); // Chrome: "", IE9: "foobar", FF3.5: "foobar"
        1003 * </pre>
        1004 *
        1005 * For more information, see:
        1006 * http://dev.w3.org/html5/markup/syntax.html#syntax-elements
        1007 *
        1008 * TODO(user): Rename shouldAllowChildren() ?
        1009 *
        1010 * @param {Node} node The node to check.
        1011 * @return {boolean} Whether the node can contain children.
        1012 */
        1013goog.dom.canHaveChildren = function(node) {
        1014 if (node.nodeType != goog.dom.NodeType.ELEMENT) {
        1015 return false;
        1016 }
        1017 switch (/** @type {!Element} */ (node).tagName) {
        1018 case goog.dom.TagName.APPLET:
        1019 case goog.dom.TagName.AREA:
        1020 case goog.dom.TagName.BASE:
        1021 case goog.dom.TagName.BR:
        1022 case goog.dom.TagName.COL:
        1023 case goog.dom.TagName.COMMAND:
        1024 case goog.dom.TagName.EMBED:
        1025 case goog.dom.TagName.FRAME:
        1026 case goog.dom.TagName.HR:
        1027 case goog.dom.TagName.IMG:
        1028 case goog.dom.TagName.INPUT:
        1029 case goog.dom.TagName.IFRAME:
        1030 case goog.dom.TagName.ISINDEX:
        1031 case goog.dom.TagName.KEYGEN:
        1032 case goog.dom.TagName.LINK:
        1033 case goog.dom.TagName.NOFRAMES:
        1034 case goog.dom.TagName.NOSCRIPT:
        1035 case goog.dom.TagName.META:
        1036 case goog.dom.TagName.OBJECT:
        1037 case goog.dom.TagName.PARAM:
        1038 case goog.dom.TagName.SCRIPT:
        1039 case goog.dom.TagName.SOURCE:
        1040 case goog.dom.TagName.STYLE:
        1041 case goog.dom.TagName.TRACK:
        1042 case goog.dom.TagName.WBR:
        1043 return false;
        1044 }
        1045 return true;
        1046};
        1047
        1048
        1049/**
        1050 * Appends a child to a node.
        1051 * @param {Node} parent Parent.
        1052 * @param {Node} child Child.
        1053 */
        1054goog.dom.appendChild = function(parent, child) {
        1055 parent.appendChild(child);
        1056};
        1057
        1058
        1059/**
        1060 * Appends a node with text or other nodes.
        1061 * @param {!Node} parent The node to append nodes to.
        1062 * @param {...goog.dom.Appendable} var_args The things to append to the node.
        1063 * If this is a Node it is appended as is.
        1064 * If this is a string then a text node is appended.
        1065 * If this is an array like object then fields 0 to length - 1 are appended.
        1066 */
        1067goog.dom.append = function(parent, var_args) {
        1068 goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1);
        1069};
        1070
        1071
        1072/**
        1073 * Removes all the child nodes on a DOM node.
        1074 * @param {Node} node Node to remove children from.
        1075 */
        1076goog.dom.removeChildren = function(node) {
        1077 // Note: Iterations over live collections can be slow, this is the fastest
        1078 // we could find. The double parenthesis are used to prevent JsCompiler and
        1079 // strict warnings.
        1080 var child;
        1081 while ((child = node.firstChild)) {
        1082 node.removeChild(child);
        1083 }
        1084};
        1085
        1086
        1087/**
        1088 * Inserts a new node before an existing reference node (i.e. as the previous
        1089 * sibling). If the reference node has no parent, then does nothing.
        1090 * @param {Node} newNode Node to insert.
        1091 * @param {Node} refNode Reference node to insert before.
        1092 */
        1093goog.dom.insertSiblingBefore = function(newNode, refNode) {
        1094 if (refNode.parentNode) {
        1095 refNode.parentNode.insertBefore(newNode, refNode);
        1096 }
        1097};
        1098
        1099
        1100/**
        1101 * Inserts a new node after an existing reference node (i.e. as the next
        1102 * sibling). If the reference node has no parent, then does nothing.
        1103 * @param {Node} newNode Node to insert.
        1104 * @param {Node} refNode Reference node to insert after.
        1105 */
        1106goog.dom.insertSiblingAfter = function(newNode, refNode) {
        1107 if (refNode.parentNode) {
        1108 refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
        1109 }
        1110};
        1111
        1112
        1113/**
        1114 * Insert a child at a given index. If index is larger than the number of child
        1115 * nodes that the parent currently has, the node is inserted as the last child
        1116 * node.
        1117 * @param {Element} parent The element into which to insert the child.
        1118 * @param {Node} child The element to insert.
        1119 * @param {number} index The index at which to insert the new child node. Must
        1120 * not be negative.
        1121 */
        1122goog.dom.insertChildAt = function(parent, child, index) {
        1123 // Note that if the second argument is null, insertBefore
        1124 // will append the child at the end of the list of children.
        1125 parent.insertBefore(child, parent.childNodes[index] || null);
        1126};
        1127
        1128
        1129/**
        1130 * Removes a node from its parent.
        1131 * @param {Node} node The node to remove.
        1132 * @return {Node} The node removed if removed; else, null.
        1133 */
        1134goog.dom.removeNode = function(node) {
        1135 return node && node.parentNode ? node.parentNode.removeChild(node) : null;
        1136};
        1137
        1138
        1139/**
        1140 * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
        1141 * parent.
        1142 * @param {Node} newNode Node to insert.
        1143 * @param {Node} oldNode Node to replace.
        1144 */
        1145goog.dom.replaceNode = function(newNode, oldNode) {
        1146 var parent = oldNode.parentNode;
        1147 if (parent) {
        1148 parent.replaceChild(newNode, oldNode);
        1149 }
        1150};
        1151
        1152
        1153/**
        1154 * Flattens an element. That is, removes it and replace it with its children.
        1155 * Does nothing if the element is not in the document.
        1156 * @param {Element} element The element to flatten.
        1157 * @return {Element|undefined} The original element, detached from the document
        1158 * tree, sans children; or undefined, if the element was not in the document
        1159 * to begin with.
        1160 */
        1161goog.dom.flattenElement = function(element) {
        1162 var child, parent = element.parentNode;
        1163 if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {
        1164 // Use IE DOM method (supported by Opera too) if available
        1165 if (element.removeNode) {
        1166 return /** @type {Element} */ (element.removeNode(false));
        1167 } else {
        1168 // Move all children of the original node up one level.
        1169 while ((child = element.firstChild)) {
        1170 parent.insertBefore(child, element);
        1171 }
        1172
        1173 // Detach the original element.
        1174 return /** @type {Element} */ (goog.dom.removeNode(element));
        1175 }
        1176 }
        1177};
        1178
        1179
        1180/**
        1181 * Returns an array containing just the element children of the given element.
        1182 * @param {Element} element The element whose element children we want.
        1183 * @return {!(Array|NodeList)} An array or array-like list of just the element
        1184 * children of the given element.
        1185 */
        1186goog.dom.getChildren = function(element) {
        1187 // We check if the children attribute is supported for child elements
        1188 // since IE8 misuses the attribute by also including comments.
        1189 if (goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE &&
        1190 element.children != undefined) {
        1191 return element.children;
        1192 }
        1193 // Fall back to manually filtering the element's child nodes.
        1194 return goog.array.filter(element.childNodes, function(node) {
        1195 return node.nodeType == goog.dom.NodeType.ELEMENT;
        1196 });
        1197};
        1198
        1199
        1200/**
        1201 * Returns the first child node that is an element.
        1202 * @param {Node} node The node to get the first child element of.
        1203 * @return {Element} The first child node of {@code node} that is an element.
        1204 */
        1205goog.dom.getFirstElementChild = function(node) {
        1206 if (goog.isDef(node.firstElementChild)) {
        1207 return /** @type {!Element} */(node).firstElementChild;
        1208 }
        1209 return goog.dom.getNextElementNode_(node.firstChild, true);
        1210};
        1211
        1212
        1213/**
        1214 * Returns the last child node that is an element.
        1215 * @param {Node} node The node to get the last child element of.
        1216 * @return {Element} The last child node of {@code node} that is an element.
        1217 */
        1218goog.dom.getLastElementChild = function(node) {
        1219 if (goog.isDef(node.lastElementChild)) {
        1220 return /** @type {!Element} */(node).lastElementChild;
        1221 }
        1222 return goog.dom.getNextElementNode_(node.lastChild, false);
        1223};
        1224
        1225
        1226/**
        1227 * Returns the first next sibling that is an element.
        1228 * @param {Node} node The node to get the next sibling element of.
        1229 * @return {Element} The next sibling of {@code node} that is an element.
        1230 */
        1231goog.dom.getNextElementSibling = function(node) {
        1232 if (goog.isDef(node.nextElementSibling)) {
        1233 return /** @type {!Element} */(node).nextElementSibling;
        1234 }
        1235 return goog.dom.getNextElementNode_(node.nextSibling, true);
        1236};
        1237
        1238
        1239/**
        1240 * Returns the first previous sibling that is an element.
        1241 * @param {Node} node The node to get the previous sibling element of.
        1242 * @return {Element} The first previous sibling of {@code node} that is
        1243 * an element.
        1244 */
        1245goog.dom.getPreviousElementSibling = function(node) {
        1246 if (goog.isDef(node.previousElementSibling)) {
        1247 return /** @type {!Element} */(node).previousElementSibling;
        1248 }
        1249 return goog.dom.getNextElementNode_(node.previousSibling, false);
        1250};
        1251
        1252
        1253/**
        1254 * Returns the first node that is an element in the specified direction,
        1255 * starting with {@code node}.
        1256 * @param {Node} node The node to get the next element from.
        1257 * @param {boolean} forward Whether to look forwards or backwards.
        1258 * @return {Element} The first element.
        1259 * @private
        1260 */
        1261goog.dom.getNextElementNode_ = function(node, forward) {
        1262 while (node && node.nodeType != goog.dom.NodeType.ELEMENT) {
        1263 node = forward ? node.nextSibling : node.previousSibling;
        1264 }
        1265
        1266 return /** @type {Element} */ (node);
        1267};
        1268
        1269
        1270/**
        1271 * Returns the next node in source order from the given node.
        1272 * @param {Node} node The node.
        1273 * @return {Node} The next node in the DOM tree, or null if this was the last
        1274 * node.
        1275 */
        1276goog.dom.getNextNode = function(node) {
        1277 if (!node) {
        1278 return null;
        1279 }
        1280
        1281 if (node.firstChild) {
        1282 return node.firstChild;
        1283 }
        1284
        1285 while (node && !node.nextSibling) {
        1286 node = node.parentNode;
        1287 }
        1288
        1289 return node ? node.nextSibling : null;
        1290};
        1291
        1292
        1293/**
        1294 * Returns the previous node in source order from the given node.
        1295 * @param {Node} node The node.
        1296 * @return {Node} The previous node in the DOM tree, or null if this was the
        1297 * first node.
        1298 */
        1299goog.dom.getPreviousNode = function(node) {
        1300 if (!node) {
        1301 return null;
        1302 }
        1303
        1304 if (!node.previousSibling) {
        1305 return node.parentNode;
        1306 }
        1307
        1308 node = node.previousSibling;
        1309 while (node && node.lastChild) {
        1310 node = node.lastChild;
        1311 }
        1312
        1313 return node;
        1314};
        1315
        1316
        1317/**
        1318 * Whether the object looks like a DOM node.
        1319 * @param {?} obj The object being tested for node likeness.
        1320 * @return {boolean} Whether the object looks like a DOM node.
        1321 */
        1322goog.dom.isNodeLike = function(obj) {
        1323 return goog.isObject(obj) && obj.nodeType > 0;
        1324};
        1325
        1326
        1327/**
        1328 * Whether the object looks like an Element.
        1329 * @param {?} obj The object being tested for Element likeness.
        1330 * @return {boolean} Whether the object looks like an Element.
        1331 */
        1332goog.dom.isElement = function(obj) {
        1333 return goog.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT;
        1334};
        1335
        1336
        1337/**
        1338 * Returns true if the specified value is a Window object. This includes the
        1339 * global window for HTML pages, and iframe windows.
        1340 * @param {?} obj Variable to test.
        1341 * @return {boolean} Whether the variable is a window.
        1342 */
        1343goog.dom.isWindow = function(obj) {
        1344 return goog.isObject(obj) && obj['window'] == obj;
        1345};
        1346
        1347
        1348/**
        1349 * Returns an element's parent, if it's an Element.
        1350 * @param {Element} element The DOM element.
        1351 * @return {Element} The parent, or null if not an Element.
        1352 */
        1353goog.dom.getParentElement = function(element) {
        1354 var parent;
        1355 if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) {
        1356 var isIe9 = goog.userAgent.IE &&
        1357 goog.userAgent.isVersionOrHigher('9') &&
        1358 !goog.userAgent.isVersionOrHigher('10');
        1359 // SVG elements in IE9 can't use the parentElement property.
        1360 // goog.global['SVGElement'] is not defined in IE9 quirks mode.
        1361 if (!(isIe9 && goog.global['SVGElement'] &&
        1362 element instanceof goog.global['SVGElement'])) {
        1363 parent = element.parentElement;
        1364 if (parent) {
        1365 return parent;
        1366 }
        1367 }
        1368 }
        1369 parent = element.parentNode;
        1370 return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null;
        1371};
        1372
        1373
        1374/**
        1375 * Whether a node contains another node.
        1376 * @param {Node} parent The node that should contain the other node.
        1377 * @param {Node} descendant The node to test presence of.
        1378 * @return {boolean} Whether the parent node contains the descendent node.
        1379 */
        1380goog.dom.contains = function(parent, descendant) {
        1381 // We use browser specific methods for this if available since it is faster
        1382 // that way.
        1383
        1384 // IE DOM
        1385 if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {
        1386 return parent == descendant || parent.contains(descendant);
        1387 }
        1388
        1389 // W3C DOM Level 3
        1390 if (typeof parent.compareDocumentPosition != 'undefined') {
        1391 return parent == descendant ||
        1392 Boolean(parent.compareDocumentPosition(descendant) & 16);
        1393 }
        1394
        1395 // W3C DOM Level 1
        1396 while (descendant && parent != descendant) {
        1397 descendant = descendant.parentNode;
        1398 }
        1399 return descendant == parent;
        1400};
        1401
        1402
        1403/**
        1404 * Compares the document order of two nodes, returning 0 if they are the same
        1405 * node, a negative number if node1 is before node2, and a positive number if
        1406 * node2 is before node1. Note that we compare the order the tags appear in the
        1407 * document so in the tree <b><i>text</i></b> the B node is considered to be
        1408 * before the I node.
        1409 *
        1410 * @param {Node} node1 The first node to compare.
        1411 * @param {Node} node2 The second node to compare.
        1412 * @return {number} 0 if the nodes are the same node, a negative number if node1
        1413 * is before node2, and a positive number if node2 is before node1.
        1414 */
        1415goog.dom.compareNodeOrder = function(node1, node2) {
        1416 // Fall out quickly for equality.
        1417 if (node1 == node2) {
        1418 return 0;
        1419 }
        1420
        1421 // Use compareDocumentPosition where available
        1422 if (node1.compareDocumentPosition) {
        1423 // 4 is the bitmask for FOLLOWS.
        1424 return node1.compareDocumentPosition(node2) & 2 ? 1 : -1;
        1425 }
        1426
        1427 // Special case for document nodes on IE 7 and 8.
        1428 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
        1429 if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {
        1430 return -1;
        1431 }
        1432 if (node2.nodeType == goog.dom.NodeType.DOCUMENT) {
        1433 return 1;
        1434 }
        1435 }
        1436
        1437 // Process in IE using sourceIndex - we check to see if the first node has
        1438 // a source index or if its parent has one.
        1439 if ('sourceIndex' in node1 ||
        1440 (node1.parentNode && 'sourceIndex' in node1.parentNode)) {
        1441 var isElement1 = node1.nodeType == goog.dom.NodeType.ELEMENT;
        1442 var isElement2 = node2.nodeType == goog.dom.NodeType.ELEMENT;
        1443
        1444 if (isElement1 && isElement2) {
        1445 return node1.sourceIndex - node2.sourceIndex;
        1446 } else {
        1447 var parent1 = node1.parentNode;
        1448 var parent2 = node2.parentNode;
        1449
        1450 if (parent1 == parent2) {
        1451 return goog.dom.compareSiblingOrder_(node1, node2);
        1452 }
        1453
        1454 if (!isElement1 && goog.dom.contains(parent1, node2)) {
        1455 return -1 * goog.dom.compareParentsDescendantNodeIe_(node1, node2);
        1456 }
        1457
        1458
        1459 if (!isElement2 && goog.dom.contains(parent2, node1)) {
        1460 return goog.dom.compareParentsDescendantNodeIe_(node2, node1);
        1461 }
        1462
        1463 return (isElement1 ? node1.sourceIndex : parent1.sourceIndex) -
        1464 (isElement2 ? node2.sourceIndex : parent2.sourceIndex);
        1465 }
        1466 }
        1467
        1468 // For Safari, we compare ranges.
        1469 var doc = goog.dom.getOwnerDocument(node1);
        1470
        1471 var range1, range2;
        1472 range1 = doc.createRange();
        1473 range1.selectNode(node1);
        1474 range1.collapse(true);
        1475
        1476 range2 = doc.createRange();
        1477 range2.selectNode(node2);
        1478 range2.collapse(true);
        1479
        1480 return range1.compareBoundaryPoints(goog.global['Range'].START_TO_END,
        1481 range2);
        1482};
        1483
        1484
        1485/**
        1486 * Utility function to compare the position of two nodes, when
        1487 * {@code textNode}'s parent is an ancestor of {@code node}. If this entry
        1488 * condition is not met, this function will attempt to reference a null object.
        1489 * @param {!Node} textNode The textNode to compare.
        1490 * @param {Node} node The node to compare.
        1491 * @return {number} -1 if node is before textNode, +1 otherwise.
        1492 * @private
        1493 */
        1494goog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) {
        1495 var parent = textNode.parentNode;
        1496 if (parent == node) {
        1497 // If textNode is a child of node, then node comes first.
        1498 return -1;
        1499 }
        1500 var sibling = node;
        1501 while (sibling.parentNode != parent) {
        1502 sibling = sibling.parentNode;
        1503 }
        1504 return goog.dom.compareSiblingOrder_(sibling, textNode);
        1505};
        1506
        1507
        1508/**
        1509 * Utility function to compare the position of two nodes known to be non-equal
        1510 * siblings.
        1511 * @param {Node} node1 The first node to compare.
        1512 * @param {!Node} node2 The second node to compare.
        1513 * @return {number} -1 if node1 is before node2, +1 otherwise.
        1514 * @private
        1515 */
        1516goog.dom.compareSiblingOrder_ = function(node1, node2) {
        1517 var s = node2;
        1518 while ((s = s.previousSibling)) {
        1519 if (s == node1) {
        1520 // We just found node1 before node2.
        1521 return -1;
        1522 }
        1523 }
        1524
        1525 // Since we didn't find it, node1 must be after node2.
        1526 return 1;
        1527};
        1528
        1529
        1530/**
        1531 * Find the deepest common ancestor of the given nodes.
        1532 * @param {...Node} var_args The nodes to find a common ancestor of.
        1533 * @return {Node} The common ancestor of the nodes, or null if there is none.
        1534 * null will only be returned if two or more of the nodes are from different
        1535 * documents.
        1536 */
        1537goog.dom.findCommonAncestor = function(var_args) {
        1538 var i, count = arguments.length;
        1539 if (!count) {
        1540 return null;
        1541 } else if (count == 1) {
        1542 return arguments[0];
        1543 }
        1544
        1545 var paths = [];
        1546 var minLength = Infinity;
        1547 for (i = 0; i < count; i++) {
        1548 // Compute the list of ancestors.
        1549 var ancestors = [];
        1550 var node = arguments[i];
        1551 while (node) {
        1552 ancestors.unshift(node);
        1553 node = node.parentNode;
        1554 }
        1555
        1556 // Save the list for comparison.
        1557 paths.push(ancestors);
        1558 minLength = Math.min(minLength, ancestors.length);
        1559 }
        1560 var output = null;
        1561 for (i = 0; i < minLength; i++) {
        1562 var first = paths[0][i];
        1563 for (var j = 1; j < count; j++) {
        1564 if (first != paths[j][i]) {
        1565 return output;
        1566 }
        1567 }
        1568 output = first;
        1569 }
        1570 return output;
        1571};
        1572
        1573
        1574/**
        1575 * Returns the owner document for a node.
        1576 * @param {Node|Window} node The node to get the document for.
        1577 * @return {!Document} The document owning the node.
        1578 */
        1579goog.dom.getOwnerDocument = function(node) {
        1580 // TODO(nnaze): Update param signature to be non-nullable.
        1581 goog.asserts.assert(node, 'Node cannot be null or undefined.');
        1582 return /** @type {!Document} */ (
        1583 node.nodeType == goog.dom.NodeType.DOCUMENT ? node :
        1584 node.ownerDocument || node.document);
        1585};
        1586
        1587
        1588/**
        1589 * Cross-browser function for getting the document element of a frame or iframe.
        1590 * @param {Element} frame Frame element.
        1591 * @return {!Document} The frame content document.
        1592 */
        1593goog.dom.getFrameContentDocument = function(frame) {
        1594 var doc = frame.contentDocument || frame.contentWindow.document;
        1595 return doc;
        1596};
        1597
        1598
        1599/**
        1600 * Cross-browser function for getting the window of a frame or iframe.
        1601 * @param {Element} frame Frame element.
        1602 * @return {Window} The window associated with the given frame.
        1603 */
        1604goog.dom.getFrameContentWindow = function(frame) {
        1605 return frame.contentWindow ||
        1606 goog.dom.getWindow(goog.dom.getFrameContentDocument(frame));
        1607};
        1608
        1609
        1610/**
        1611 * Sets the text content of a node, with cross-browser support.
        1612 * @param {Node} node The node to change the text content of.
        1613 * @param {string|number} text The value that should replace the node's content.
        1614 */
        1615goog.dom.setTextContent = function(node, text) {
        1616 goog.asserts.assert(node != null,
        1617 'goog.dom.setTextContent expects a non-null value for node');
        1618
        1619 if ('textContent' in node) {
        1620 node.textContent = text;
        1621 } else if (node.nodeType == goog.dom.NodeType.TEXT) {
        1622 node.data = text;
        1623 } else if (node.firstChild &&
        1624 node.firstChild.nodeType == goog.dom.NodeType.TEXT) {
        1625 // If the first child is a text node we just change its data and remove the
        1626 // rest of the children.
        1627 while (node.lastChild != node.firstChild) {
        1628 node.removeChild(node.lastChild);
        1629 }
        1630 node.firstChild.data = text;
        1631 } else {
        1632 goog.dom.removeChildren(node);
        1633 var doc = goog.dom.getOwnerDocument(node);
        1634 node.appendChild(doc.createTextNode(String(text)));
        1635 }
        1636};
        1637
        1638
        1639/**
        1640 * Gets the outerHTML of a node, which islike innerHTML, except that it
        1641 * actually contains the HTML of the node itself.
        1642 * @param {Element} element The element to get the HTML of.
        1643 * @return {string} The outerHTML of the given element.
        1644 */
        1645goog.dom.getOuterHtml = function(element) {
        1646 // IE, Opera and WebKit all have outerHTML.
        1647 if ('outerHTML' in element) {
        1648 return element.outerHTML;
        1649 } else {
        1650 var doc = goog.dom.getOwnerDocument(element);
        1651 var div = doc.createElement(goog.dom.TagName.DIV);
        1652 div.appendChild(element.cloneNode(true));
        1653 return div.innerHTML;
        1654 }
        1655};
        1656
        1657
        1658/**
        1659 * Finds the first descendant node that matches the filter function, using
        1660 * a depth first search. This function offers the most general purpose way
        1661 * of finding a matching element. You may also wish to consider
        1662 * {@code goog.dom.query} which can express many matching criteria using
        1663 * CSS selector expressions. These expressions often result in a more
        1664 * compact representation of the desired result.
        1665 * @see goog.dom.query
        1666 *
        1667 * @param {Node} root The root of the tree to search.
        1668 * @param {function(Node) : boolean} p The filter function.
        1669 * @return {Node|undefined} The found node or undefined if none is found.
        1670 */
        1671goog.dom.findNode = function(root, p) {
        1672 var rv = [];
        1673 var found = goog.dom.findNodes_(root, p, rv, true);
        1674 return found ? rv[0] : undefined;
        1675};
        1676
        1677
        1678/**
        1679 * Finds all the descendant nodes that match the filter function, using a
        1680 * a depth first search. This function offers the most general-purpose way
        1681 * of finding a set of matching elements. You may also wish to consider
        1682 * {@code goog.dom.query} which can express many matching criteria using
        1683 * CSS selector expressions. These expressions often result in a more
        1684 * compact representation of the desired result.
        1685
        1686 * @param {Node} root The root of the tree to search.
        1687 * @param {function(Node) : boolean} p The filter function.
        1688 * @return {!Array<!Node>} The found nodes or an empty array if none are found.
        1689 */
        1690goog.dom.findNodes = function(root, p) {
        1691 var rv = [];
        1692 goog.dom.findNodes_(root, p, rv, false);
        1693 return rv;
        1694};
        1695
        1696
        1697/**
        1698 * Finds the first or all the descendant nodes that match the filter function,
        1699 * using a depth first search.
        1700 * @param {Node} root The root of the tree to search.
        1701 * @param {function(Node) : boolean} p The filter function.
        1702 * @param {!Array<!Node>} rv The found nodes are added to this array.
        1703 * @param {boolean} findOne If true we exit after the first found node.
        1704 * @return {boolean} Whether the search is complete or not. True in case findOne
        1705 * is true and the node is found. False otherwise.
        1706 * @private
        1707 */
        1708goog.dom.findNodes_ = function(root, p, rv, findOne) {
        1709 if (root != null) {
        1710 var child = root.firstChild;
        1711 while (child) {
        1712 if (p(child)) {
        1713 rv.push(child);
        1714 if (findOne) {
        1715 return true;
        1716 }
        1717 }
        1718 if (goog.dom.findNodes_(child, p, rv, findOne)) {
        1719 return true;
        1720 }
        1721 child = child.nextSibling;
        1722 }
        1723 }
        1724 return false;
        1725};
        1726
        1727
        1728/**
        1729 * Map of tags whose content to ignore when calculating text length.
        1730 * @private {!Object<string, number>}
        1731 * @const
        1732 */
        1733goog.dom.TAGS_TO_IGNORE_ = {
        1734 'SCRIPT': 1,
        1735 'STYLE': 1,
        1736 'HEAD': 1,
        1737 'IFRAME': 1,
        1738 'OBJECT': 1
        1739};
        1740
        1741
        1742/**
        1743 * Map of tags which have predefined values with regard to whitespace.
        1744 * @private {!Object<string, string>}
        1745 * @const
        1746 */
        1747goog.dom.PREDEFINED_TAG_VALUES_ = {'IMG': ' ', 'BR': '\n'};
        1748
        1749
        1750/**
        1751 * Returns true if the element has a tab index that allows it to receive
        1752 * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
        1753 * natively support keyboard focus, even if they have no tab index.
        1754 * @param {!Element} element Element to check.
        1755 * @return {boolean} Whether the element has a tab index that allows keyboard
        1756 * focus.
        1757 * @see http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
        1758 */
        1759goog.dom.isFocusableTabIndex = function(element) {
        1760 return goog.dom.hasSpecifiedTabIndex_(element) &&
        1761 goog.dom.isTabIndexFocusable_(element);
        1762};
        1763
        1764
        1765/**
        1766 * Enables or disables keyboard focus support on the element via its tab index.
        1767 * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
        1768 * (or elements that natively support keyboard focus, like form elements) can
        1769 * receive keyboard focus. See http://go/tabindex for more info.
        1770 * @param {Element} element Element whose tab index is to be changed.
        1771 * @param {boolean} enable Whether to set or remove a tab index on the element
        1772 * that supports keyboard focus.
        1773 */
        1774goog.dom.setFocusableTabIndex = function(element, enable) {
        1775 if (enable) {
        1776 element.tabIndex = 0;
        1777 } else {
        1778 // Set tabIndex to -1 first, then remove it. This is a workaround for
        1779 // Safari (confirmed in version 4 on Windows). When removing the attribute
        1780 // without setting it to -1 first, the element remains keyboard focusable
        1781 // despite not having a tabIndex attribute anymore.
        1782 element.tabIndex = -1;
        1783 element.removeAttribute('tabIndex'); // Must be camelCase!
        1784 }
        1785};
        1786
        1787
        1788/**
        1789 * Returns true if the element can be focused, i.e. it has a tab index that
        1790 * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
        1791 * that natively supports keyboard focus.
        1792 * @param {!Element} element Element to check.
        1793 * @return {boolean} Whether the element allows keyboard focus.
        1794 */
        1795goog.dom.isFocusable = function(element) {
        1796 var focusable;
        1797 // Some elements can have unspecified tab index and still receive focus.
        1798 if (goog.dom.nativelySupportsFocus_(element)) {
        1799 // Make sure the element is not disabled ...
        1800 focusable = !element.disabled &&
        1801 // ... and if a tab index is specified, it allows focus.
        1802 (!goog.dom.hasSpecifiedTabIndex_(element) ||
        1803 goog.dom.isTabIndexFocusable_(element));
        1804 } else {
        1805 focusable = goog.dom.isFocusableTabIndex(element);
        1806 }
        1807
        1808 // IE requires elements to be visible in order to focus them.
        1809 return focusable && goog.userAgent.IE ?
        1810 goog.dom.hasNonZeroBoundingRect_(element) : focusable;
        1811};
        1812
        1813
        1814/**
        1815 * Returns true if the element has a specified tab index.
        1816 * @param {!Element} element Element to check.
        1817 * @return {boolean} Whether the element has a specified tab index.
        1818 * @private
        1819 */
        1820goog.dom.hasSpecifiedTabIndex_ = function(element) {
        1821 // IE returns 0 for an unset tabIndex, so we must use getAttributeNode(),
        1822 // which returns an object with a 'specified' property if tabIndex is
        1823 // specified. This works on other browsers, too.
        1824 var attrNode = element.getAttributeNode('tabindex'); // Must be lowercase!
        1825 return goog.isDefAndNotNull(attrNode) && attrNode.specified;
        1826};
        1827
        1828
        1829/**
        1830 * Returns true if the element's tab index allows the element to be focused.
        1831 * @param {!Element} element Element to check.
        1832 * @return {boolean} Whether the element's tab index allows focus.
        1833 * @private
        1834 */
        1835goog.dom.isTabIndexFocusable_ = function(element) {
        1836 var index = element.tabIndex;
        1837 // NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534.
        1838 return goog.isNumber(index) && index >= 0 && index < 32768;
        1839};
        1840
        1841
        1842/**
        1843 * Returns true if the element is focusable even when tabIndex is not set.
        1844 * @param {!Element} element Element to check.
        1845 * @return {boolean} Whether the element natively supports focus.
        1846 * @private
        1847 */
        1848goog.dom.nativelySupportsFocus_ = function(element) {
        1849 return element.tagName == goog.dom.TagName.A ||
        1850 element.tagName == goog.dom.TagName.INPUT ||
        1851 element.tagName == goog.dom.TagName.TEXTAREA ||
        1852 element.tagName == goog.dom.TagName.SELECT ||
        1853 element.tagName == goog.dom.TagName.BUTTON;
        1854};
        1855
        1856
        1857/**
        1858 * Returns true if the element has a bounding rectangle that would be visible
        1859 * (i.e. its width and height are greater than zero).
        1860 * @param {!Element} element Element to check.
        1861 * @return {boolean} Whether the element has a non-zero bounding rectangle.
        1862 * @private
        1863 */
        1864goog.dom.hasNonZeroBoundingRect_ = function(element) {
        1865 var rect = goog.isFunction(element['getBoundingClientRect']) ?
        1866 element.getBoundingClientRect() :
        1867 {'height': element.offsetHeight, 'width': element.offsetWidth};
        1868 return goog.isDefAndNotNull(rect) && rect.height > 0 && rect.width > 0;
        1869};
        1870
        1871
        1872/**
        1873 * Returns the text content of the current node, without markup and invisible
        1874 * symbols. New lines are stripped and whitespace is collapsed,
        1875 * such that each character would be visible.
        1876 *
        1877 * In browsers that support it, innerText is used. Other browsers attempt to
        1878 * simulate it via node traversal. Line breaks are canonicalized in IE.
        1879 *
        1880 * @param {Node} node The node from which we are getting content.
        1881 * @return {string} The text content.
        1882 */
        1883goog.dom.getTextContent = function(node) {
        1884 var textContent;
        1885 // Note(arv): IE9, Opera, and Safari 3 support innerText but they include
        1886 // text nodes in script tags. So we revert to use a user agent test here.
        1887 if (goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && ('innerText' in node)) {
        1888 textContent = goog.string.canonicalizeNewlines(node.innerText);
        1889 // Unfortunately .innerText() returns text with &shy; symbols
        1890 // We need to filter it out and then remove duplicate whitespaces
        1891 } else {
        1892 var buf = [];
        1893 goog.dom.getTextContent_(node, buf, true);
        1894 textContent = buf.join('');
        1895 }
        1896
        1897 // Strip &shy; entities. goog.format.insertWordBreaks inserts them in Opera.
        1898 textContent = textContent.replace(/ \xAD /g, ' ').replace(/\xAD/g, '');
        1899 // Strip &#8203; entities. goog.format.insertWordBreaks inserts them in IE8.
        1900 textContent = textContent.replace(/\u200B/g, '');
        1901
        1902 // Skip this replacement on old browsers with working innerText, which
        1903 // automatically turns &nbsp; into ' ' and / +/ into ' ' when reading
        1904 // innerText.
        1905 if (!goog.dom.BrowserFeature.CAN_USE_INNER_TEXT) {
        1906 textContent = textContent.replace(/ +/g, ' ');
        1907 }
        1908 if (textContent != ' ') {
        1909 textContent = textContent.replace(/^\s*/, '');
        1910 }
        1911
        1912 return textContent;
        1913};
        1914
        1915
        1916/**
        1917 * Returns the text content of the current node, without markup.
        1918 *
        1919 * Unlike {@code getTextContent} this method does not collapse whitespaces
        1920 * or normalize lines breaks.
        1921 *
        1922 * @param {Node} node The node from which we are getting content.
        1923 * @return {string} The raw text content.
        1924 */
        1925goog.dom.getRawTextContent = function(node) {
        1926 var buf = [];
        1927 goog.dom.getTextContent_(node, buf, false);
        1928
        1929 return buf.join('');
        1930};
        1931
        1932
        1933/**
        1934 * Recursive support function for text content retrieval.
        1935 *
        1936 * @param {Node} node The node from which we are getting content.
        1937 * @param {Array<string>} buf string buffer.
        1938 * @param {boolean} normalizeWhitespace Whether to normalize whitespace.
        1939 * @private
        1940 */
        1941goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {
        1942 if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) {
        1943 // ignore certain tags
        1944 } else if (node.nodeType == goog.dom.NodeType.TEXT) {
        1945 if (normalizeWhitespace) {
        1946 buf.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, ''));
        1947 } else {
        1948 buf.push(node.nodeValue);
        1949 }
        1950 } else if (node.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
        1951 buf.push(goog.dom.PREDEFINED_TAG_VALUES_[node.nodeName]);
        1952 } else {
        1953 var child = node.firstChild;
        1954 while (child) {
        1955 goog.dom.getTextContent_(child, buf, normalizeWhitespace);
        1956 child = child.nextSibling;
        1957 }
        1958 }
        1959};
        1960
        1961
        1962/**
        1963 * Returns the text length of the text contained in a node, without markup. This
        1964 * is equivalent to the selection length if the node was selected, or the number
        1965 * of cursor movements to traverse the node. Images & BRs take one space. New
        1966 * lines are ignored.
        1967 *
        1968 * @param {Node} node The node whose text content length is being calculated.
        1969 * @return {number} The length of {@code node}'s text content.
        1970 */
        1971goog.dom.getNodeTextLength = function(node) {
        1972 return goog.dom.getTextContent(node).length;
        1973};
        1974
        1975
        1976/**
        1977 * Returns the text offset of a node relative to one of its ancestors. The text
        1978 * length is the same as the length calculated by goog.dom.getNodeTextLength.
        1979 *
        1980 * @param {Node} node The node whose offset is being calculated.
        1981 * @param {Node=} opt_offsetParent The node relative to which the offset will
        1982 * be calculated. Defaults to the node's owner document's body.
        1983 * @return {number} The text offset.
        1984 */
        1985goog.dom.getNodeTextOffset = function(node, opt_offsetParent) {
        1986 var root = opt_offsetParent || goog.dom.getOwnerDocument(node).body;
        1987 var buf = [];
        1988 while (node && node != root) {
        1989 var cur = node;
        1990 while ((cur = cur.previousSibling)) {
        1991 buf.unshift(goog.dom.getTextContent(cur));
        1992 }
        1993 node = node.parentNode;
        1994 }
        1995 // Trim left to deal with FF cases when there might be line breaks and empty
        1996 // nodes at the front of the text
        1997 return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length;
        1998};
        1999
        2000
        2001/**
        2002 * Returns the node at a given offset in a parent node. If an object is
        2003 * provided for the optional third parameter, the node and the remainder of the
        2004 * offset will stored as properties of this object.
        2005 * @param {Node} parent The parent node.
        2006 * @param {number} offset The offset into the parent node.
        2007 * @param {Object=} opt_result Object to be used to store the return value. The
        2008 * return value will be stored in the form {node: Node, remainder: number}
        2009 * if this object is provided.
        2010 * @return {Node} The node at the given offset.
        2011 */
        2012goog.dom.getNodeAtOffset = function(parent, offset, opt_result) {
        2013 var stack = [parent], pos = 0, cur = null;
        2014 while (stack.length > 0 && pos < offset) {
        2015 cur = stack.pop();
        2016 if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {
        2017 // ignore certain tags
        2018 } else if (cur.nodeType == goog.dom.NodeType.TEXT) {
        2019 var text = cur.nodeValue.replace(/(\r\n|\r|\n)/g, '').replace(/ +/g, ' ');
        2020 pos += text.length;
        2021 } else if (cur.nodeName in goog.dom.PREDEFINED_TAG_VALUES_) {
        2022 pos += goog.dom.PREDEFINED_TAG_VALUES_[cur.nodeName].length;
        2023 } else {
        2024 for (var i = cur.childNodes.length - 1; i >= 0; i--) {
        2025 stack.push(cur.childNodes[i]);
        2026 }
        2027 }
        2028 }
        2029 if (goog.isObject(opt_result)) {
        2030 opt_result.remainder = cur ? cur.nodeValue.length + offset - pos - 1 : 0;
        2031 opt_result.node = cur;
        2032 }
        2033
        2034 return cur;
        2035};
        2036
        2037
        2038/**
        2039 * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
        2040 * the object must have a numeric length property and an item function (which
        2041 * has type 'string' on IE for some reason).
        2042 * @param {Object} val Object to test.
        2043 * @return {boolean} Whether the object is a NodeList.
        2044 */
        2045goog.dom.isNodeList = function(val) {
        2046 // TODO(attila): Now the isNodeList is part of goog.dom we can use
        2047 // goog.userAgent to make this simpler.
        2048 // A NodeList must have a length property of type 'number' on all platforms.
        2049 if (val && typeof val.length == 'number') {
        2050 // A NodeList is an object everywhere except Safari, where it's a function.
        2051 if (goog.isObject(val)) {
        2052 // A NodeList must have an item function (on non-IE platforms) or an item
        2053 // property of type 'string' (on IE).
        2054 return typeof val.item == 'function' || typeof val.item == 'string';
        2055 } else if (goog.isFunction(val)) {
        2056 // On Safari, a NodeList is a function with an item property that is also
        2057 // a function.
        2058 return typeof val.item == 'function';
        2059 }
        2060 }
        2061
        2062 // Not a NodeList.
        2063 return false;
        2064};
        2065
        2066
        2067/**
        2068 * Walks up the DOM hierarchy returning the first ancestor that has the passed
        2069 * tag name and/or class name. If the passed element matches the specified
        2070 * criteria, the element itself is returned.
        2071 * @param {Node} element The DOM node to start with.
        2072 * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or
        2073 * null/undefined to match only based on class name).
        2074 * @param {?string=} opt_class The class name to match (or null/undefined to
        2075 * match only based on tag name).
        2076 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
        2077 * dom.
        2078 * @return {Element} The first ancestor that matches the passed criteria, or
        2079 * null if no match is found.
        2080 */
        2081goog.dom.getAncestorByTagNameAndClass = function(element, opt_tag, opt_class,
        2082 opt_maxSearchSteps) {
        2083 if (!opt_tag && !opt_class) {
        2084 return null;
        2085 }
        2086 var tagName = opt_tag ? opt_tag.toUpperCase() : null;
        2087 return /** @type {Element} */ (goog.dom.getAncestor(element,
        2088 function(node) {
        2089 return (!tagName || node.nodeName == tagName) &&
        2090 (!opt_class || goog.isString(node.className) &&
        2091 goog.array.contains(node.className.split(/\s+/), opt_class));
        2092 }, true, opt_maxSearchSteps));
        2093};
        2094
        2095
        2096/**
        2097 * Walks up the DOM hierarchy returning the first ancestor that has the passed
        2098 * class name. If the passed element matches the specified criteria, the
        2099 * element itself is returned.
        2100 * @param {Node} element The DOM node to start with.
        2101 * @param {string} className The class name to match.
        2102 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
        2103 * dom.
        2104 * @return {Element} The first ancestor that matches the passed criteria, or
        2105 * null if none match.
        2106 */
        2107goog.dom.getAncestorByClass = function(element, className, opt_maxSearchSteps) {
        2108 return goog.dom.getAncestorByTagNameAndClass(element, null, className,
        2109 opt_maxSearchSteps);
        2110};
        2111
        2112
        2113/**
        2114 * Walks up the DOM hierarchy returning the first ancestor that passes the
        2115 * matcher function.
        2116 * @param {Node} element The DOM node to start with.
        2117 * @param {function(Node) : boolean} matcher A function that returns true if the
        2118 * passed node matches the desired criteria.
        2119 * @param {boolean=} opt_includeNode If true, the node itself is included in
        2120 * the search (the first call to the matcher will pass startElement as
        2121 * the node to test).
        2122 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
        2123 * dom.
        2124 * @return {Node} DOM node that matched the matcher, or null if there was
        2125 * no match.
        2126 */
        2127goog.dom.getAncestor = function(
        2128 element, matcher, opt_includeNode, opt_maxSearchSteps) {
        2129 if (!opt_includeNode) {
        2130 element = element.parentNode;
        2131 }
        2132 var ignoreSearchSteps = opt_maxSearchSteps == null;
        2133 var steps = 0;
        2134 while (element && (ignoreSearchSteps || steps <= opt_maxSearchSteps)) {
        2135 goog.asserts.assert(element.name != 'parentNode');
        2136 if (matcher(element)) {
        2137 return element;
        2138 }
        2139 element = element.parentNode;
        2140 steps++;
        2141 }
        2142 // Reached the root of the DOM without a match
        2143 return null;
        2144};
        2145
        2146
        2147/**
        2148 * Determines the active element in the given document.
        2149 * @param {Document} doc The document to look in.
        2150 * @return {Element} The active element.
        2151 */
        2152goog.dom.getActiveElement = function(doc) {
        2153 try {
        2154 return doc && doc.activeElement;
        2155 } catch (e) {
        2156 // NOTE(nicksantos): Sometimes, evaluating document.activeElement in IE
        2157 // throws an exception. I'm not 100% sure why, but I suspect it chokes
        2158 // on document.activeElement if the activeElement has been recently
        2159 // removed from the DOM by a JS operation.
        2160 //
        2161 // We assume that an exception here simply means
        2162 // "there is no active element."
        2163 }
        2164
        2165 return null;
        2166};
        2167
        2168
        2169/**
        2170 * Gives the current devicePixelRatio.
        2171 *
        2172 * By default, this is the value of window.devicePixelRatio (which should be
        2173 * preferred if present).
        2174 *
        2175 * If window.devicePixelRatio is not present, the ratio is calculated with
        2176 * window.matchMedia, if present. Otherwise, gives 1.0.
        2177 *
        2178 * Some browsers (including Chrome) consider the browser zoom level in the pixel
        2179 * ratio, so the value may change across multiple calls.
        2180 *
        2181 * @return {number} The number of actual pixels per virtual pixel.
        2182 */
        2183goog.dom.getPixelRatio = function() {
        2184 var win = goog.dom.getWindow();
        2185 if (goog.isDef(win.devicePixelRatio)) {
        2186 return win.devicePixelRatio;
        2187 } else if (win.matchMedia) {
        2188 return goog.dom.matchesPixelRatio_(.75) ||
        2189 goog.dom.matchesPixelRatio_(1.5) ||
        2190 goog.dom.matchesPixelRatio_(2) ||
        2191 goog.dom.matchesPixelRatio_(3) || 1;
        2192 }
        2193 return 1;
        2194};
        2195
        2196
        2197/**
        2198 * Calculates a mediaQuery to check if the current device supports the
        2199 * given actual to virtual pixel ratio.
        2200 * @param {number} pixelRatio The ratio of actual pixels to virtual pixels.
        2201 * @return {number} pixelRatio if applicable, otherwise 0.
        2202 * @private
        2203 */
        2204goog.dom.matchesPixelRatio_ = function(pixelRatio) {
        2205 var win = goog.dom.getWindow();
        2206 var query = ('(-webkit-min-device-pixel-ratio: ' + pixelRatio + '),' +
        2207 '(min--moz-device-pixel-ratio: ' + pixelRatio + '),' +
        2208 '(min-resolution: ' + pixelRatio + 'dppx)');
        2209 return win.matchMedia(query).matches ? pixelRatio : 0;
        2210};
        2211
        2212
        2213
        2214/**
        2215 * Create an instance of a DOM helper with a new document object.
        2216 * @param {Document=} opt_document Document object to associate with this
        2217 * DOM helper.
        2218 * @constructor
        2219 */
        2220goog.dom.DomHelper = function(opt_document) {
        2221 /**
        2222 * Reference to the document object to use
        2223 * @type {!Document}
        2224 * @private
        2225 */
        2226 this.document_ = opt_document || goog.global.document || document;
        2227};
        2228
        2229
        2230/**
        2231 * Gets the dom helper object for the document where the element resides.
        2232 * @param {Node=} opt_node If present, gets the DomHelper for this node.
        2233 * @return {!goog.dom.DomHelper} The DomHelper.
        2234 */
        2235goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;
        2236
        2237
        2238/**
        2239 * Sets the document object.
        2240 * @param {!Document} document Document object.
        2241 */
        2242goog.dom.DomHelper.prototype.setDocument = function(document) {
        2243 this.document_ = document;
        2244};
        2245
        2246
        2247/**
        2248 * Gets the document object being used by the dom library.
        2249 * @return {!Document} Document object.
        2250 */
        2251goog.dom.DomHelper.prototype.getDocument = function() {
        2252 return this.document_;
        2253};
        2254
        2255
        2256/**
        2257 * Alias for {@code getElementById}. If a DOM node is passed in then we just
        2258 * return that.
        2259 * @param {string|Element} element Element ID or a DOM node.
        2260 * @return {Element} The element with the given ID, or the node passed in.
        2261 */
        2262goog.dom.DomHelper.prototype.getElement = function(element) {
        2263 return goog.dom.getElementHelper_(this.document_, element);
        2264};
        2265
        2266
        2267/**
        2268 * Gets an element by id, asserting that the element is found.
        2269 *
        2270 * This is used when an element is expected to exist, and should fail with
        2271 * an assertion error if it does not (if assertions are enabled).
        2272 *
        2273 * @param {string} id Element ID.
        2274 * @return {!Element} The element with the given ID, if it exists.
        2275 */
        2276goog.dom.DomHelper.prototype.getRequiredElement = function(id) {
        2277 return goog.dom.getRequiredElementHelper_(this.document_, id);
        2278};
        2279
        2280
        2281/**
        2282 * Alias for {@code getElement}.
        2283 * @param {string|Element} element Element ID or a DOM node.
        2284 * @return {Element} The element with the given ID, or the node passed in.
        2285 * @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead.
        2286 */
        2287goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;
        2288
        2289
        2290/**
        2291 * Looks up elements by both tag and class name, using browser native functions
        2292 * ({@code querySelectorAll}, {@code getElementsByTagName} or
        2293 * {@code getElementsByClassName}) where possible. The returned array is a live
        2294 * NodeList or a static list depending on the code path taken.
        2295 *
        2296 * @see goog.dom.query
        2297 *
        2298 * @param {?string=} opt_tag Element tag name or * for all tags.
        2299 * @param {?string=} opt_class Optional class name.
        2300 * @param {(Document|Element)=} opt_el Optional element to look in.
        2301 * @return { {length: number} } Array-like list of elements (only a length
        2302 * property and numerical indices are guaranteed to exist).
        2303 */
        2304goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(opt_tag,
        2305 opt_class,
        2306 opt_el) {
        2307 return goog.dom.getElementsByTagNameAndClass_(this.document_, opt_tag,
        2308 opt_class, opt_el);
        2309};
        2310
        2311
        2312/**
        2313 * Returns an array of all the elements with the provided className.
        2314 * @see {goog.dom.query}
        2315 * @param {string} className the name of the class to look for.
        2316 * @param {Element|Document=} opt_el Optional element to look in.
        2317 * @return { {length: number} } The items found with the class name provided.
        2318 */
        2319goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {
        2320 var doc = opt_el || this.document_;
        2321 return goog.dom.getElementsByClass(className, doc);
        2322};
        2323
        2324
        2325/**
        2326 * Returns the first element we find matching the provided class name.
        2327 * @see {goog.dom.query}
        2328 * @param {string} className the name of the class to look for.
        2329 * @param {(Element|Document)=} opt_el Optional element to look in.
        2330 * @return {Element} The first item found with the class name provided.
        2331 */
        2332goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {
        2333 var doc = opt_el || this.document_;
        2334 return goog.dom.getElementByClass(className, doc);
        2335};
        2336
        2337
        2338/**
        2339 * Ensures an element with the given className exists, and then returns the
        2340 * first element with the provided className.
        2341 * @see {goog.dom.query}
        2342 * @param {string} className the name of the class to look for.
        2343 * @param {(!Element|!Document)=} opt_root Optional element or document to look
        2344 * in.
        2345 * @return {!Element} The first item found with the class name provided.
        2346 * @throws {goog.asserts.AssertionError} Thrown if no element is found.
        2347 */
        2348goog.dom.DomHelper.prototype.getRequiredElementByClass = function(className,
        2349 opt_root) {
        2350 var root = opt_root || this.document_;
        2351 return goog.dom.getRequiredElementByClass(className, root);
        2352};
        2353
        2354
        2355/**
        2356 * Alias for {@code getElementsByTagNameAndClass}.
        2357 * @deprecated Use DomHelper getElementsByTagNameAndClass.
        2358 * @see goog.dom.query
        2359 *
        2360 * @param {?string=} opt_tag Element tag name.
        2361 * @param {?string=} opt_class Optional class name.
        2362 * @param {Element=} opt_el Optional element to look in.
        2363 * @return { {length: number} } Array-like list of elements (only a length
        2364 * property and numerical indices are guaranteed to exist).
        2365 */
        2366goog.dom.DomHelper.prototype.$$ =
        2367 goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;
        2368
        2369
        2370/**
        2371 * Sets a number of properties on a node.
        2372 * @param {Element} element DOM node to set properties on.
        2373 * @param {Object} properties Hash of property:value pairs.
        2374 */
        2375goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;
        2376
        2377
        2378/**
        2379 * Gets the dimensions of the viewport.
        2380 * @param {Window=} opt_window Optional window element to test. Defaults to
        2381 * the window of the Dom Helper.
        2382 * @return {!goog.math.Size} Object with values 'width' and 'height'.
        2383 */
        2384goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {
        2385 // TODO(arv): This should not take an argument. That breaks the rule of a
        2386 // a DomHelper representing a single frame/window/document.
        2387 return goog.dom.getViewportSize(opt_window || this.getWindow());
        2388};
        2389
        2390
        2391/**
        2392 * Calculates the height of the document.
        2393 *
        2394 * @return {number} The height of the document.
        2395 */
        2396goog.dom.DomHelper.prototype.getDocumentHeight = function() {
        2397 return goog.dom.getDocumentHeight_(this.getWindow());
        2398};
        2399
        2400
        2401/**
        2402 * Typedef for use with goog.dom.createDom and goog.dom.append.
        2403 * @typedef {Object|string|Array|NodeList}
        2404 */
        2405goog.dom.Appendable;
        2406
        2407
        2408/**
        2409 * Returns a dom node with a set of attributes. This function accepts varargs
        2410 * for subsequent nodes to be added. Subsequent nodes will be added to the
        2411 * first node as childNodes.
        2412 *
        2413 * So:
        2414 * <code>createDom('div', null, createDom('p'), createDom('p'));</code>
        2415 * would return a div with two child paragraphs
        2416 *
        2417 * An easy way to move all child nodes of an existing element to a new parent
        2418 * element is:
        2419 * <code>createDom('div', null, oldElement.childNodes);</code>
        2420 * which will remove all child nodes from the old element and add them as
        2421 * child nodes of the new DIV.
        2422 *
        2423 * @param {string} tagName Tag to create.
        2424 * @param {Object|string=} opt_attributes If object, then a map of name-value
        2425 * pairs for attributes. If a string, then this is the className of the new
        2426 * element.
        2427 * @param {...goog.dom.Appendable} var_args Further DOM nodes or
        2428 * strings for text nodes. If one of the var_args is an array or
        2429 * NodeList, its elements will be added as childNodes instead.
        2430 * @return {!Element} Reference to a DOM node.
        2431 */
        2432goog.dom.DomHelper.prototype.createDom = function(tagName,
        2433 opt_attributes,
        2434 var_args) {
        2435 return goog.dom.createDom_(this.document_, arguments);
        2436};
        2437
        2438
        2439/**
        2440 * Alias for {@code createDom}.
        2441 * @param {string} tagName Tag to create.
        2442 * @param {(Object|string)=} opt_attributes If object, then a map of name-value
        2443 * pairs for attributes. If a string, then this is the className of the new
        2444 * element.
        2445 * @param {...goog.dom.Appendable} var_args Further DOM nodes or strings for
        2446 * text nodes. If one of the var_args is an array, its children will be
        2447 * added as childNodes instead.
        2448 * @return {!Element} Reference to a DOM node.
        2449 * @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead.
        2450 */
        2451goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;
        2452
        2453
        2454/**
        2455 * Creates a new element.
        2456 * @param {string} name Tag name.
        2457 * @return {!Element} The new element.
        2458 */
        2459goog.dom.DomHelper.prototype.createElement = function(name) {
        2460 return this.document_.createElement(name);
        2461};
        2462
        2463
        2464/**
        2465 * Creates a new text node.
        2466 * @param {number|string} content Content.
        2467 * @return {!Text} The new text node.
        2468 */
        2469goog.dom.DomHelper.prototype.createTextNode = function(content) {
        2470 return this.document_.createTextNode(String(content));
        2471};
        2472
        2473
        2474/**
        2475 * Create a table.
        2476 * @param {number} rows The number of rows in the table. Must be >= 1.
        2477 * @param {number} columns The number of columns in the table. Must be >= 1.
        2478 * @param {boolean=} opt_fillWithNbsp If true, fills table entries with
        2479 * {@code goog.string.Unicode.NBSP} characters.
        2480 * @return {!HTMLElement} The created table.
        2481 */
        2482goog.dom.DomHelper.prototype.createTable = function(rows, columns,
        2483 opt_fillWithNbsp) {
        2484 return goog.dom.createTable_(this.document_, rows, columns,
        2485 !!opt_fillWithNbsp);
        2486};
        2487
        2488
        2489/**
        2490 * Converts an HTML into a node or a document fragment. A single Node is used if
        2491 * {@code html} only generates a single node. If {@code html} generates multiple
        2492 * nodes then these are put inside a {@code DocumentFragment}.
        2493 * @param {!goog.html.SafeHtml} html The HTML markup to convert.
        2494 * @return {!Node} The resulting node.
        2495 */
        2496goog.dom.DomHelper.prototype.safeHtmlToNode = function(html) {
        2497 return goog.dom.safeHtmlToNode_(this.document_, html);
        2498};
        2499
        2500
        2501/**
        2502 * Converts an HTML string into a node or a document fragment. A single Node
        2503 * is used if the {@code htmlString} only generates a single node. If the
        2504 * {@code htmlString} generates multiple nodes then these are put inside a
        2505 * {@code DocumentFragment}.
        2506 *
        2507 * @param {string} htmlString The HTML string to convert.
        2508 * @return {!Node} The resulting node.
        2509 */
        2510goog.dom.DomHelper.prototype.htmlToDocumentFragment = function(htmlString) {
        2511 return goog.dom.htmlToDocumentFragment_(this.document_, htmlString);
        2512};
        2513
        2514
        2515/**
        2516 * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
        2517 * mode, false otherwise.
        2518 * @return {boolean} True if in CSS1-compatible mode.
        2519 */
        2520goog.dom.DomHelper.prototype.isCss1CompatMode = function() {
        2521 return goog.dom.isCss1CompatMode_(this.document_);
        2522};
        2523
        2524
        2525/**
        2526 * Gets the window object associated with the document.
        2527 * @return {!Window} The window associated with the given document.
        2528 */
        2529goog.dom.DomHelper.prototype.getWindow = function() {
        2530 return goog.dom.getWindow_(this.document_);
        2531};
        2532
        2533
        2534/**
        2535 * Gets the document scroll element.
        2536 * @return {!Element} Scrolling element.
        2537 */
        2538goog.dom.DomHelper.prototype.getDocumentScrollElement = function() {
        2539 return goog.dom.getDocumentScrollElement_(this.document_);
        2540};
        2541
        2542
        2543/**
        2544 * Gets the document scroll distance as a coordinate object.
        2545 * @return {!goog.math.Coordinate} Object with properties 'x' and 'y'.
        2546 */
        2547goog.dom.DomHelper.prototype.getDocumentScroll = function() {
        2548 return goog.dom.getDocumentScroll_(this.document_);
        2549};
        2550
        2551
        2552/**
        2553 * Determines the active element in the given document.
        2554 * @param {Document=} opt_doc The document to look in.
        2555 * @return {Element} The active element.
        2556 */
        2557goog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) {
        2558 return goog.dom.getActiveElement(opt_doc || this.document_);
        2559};
        2560
        2561
        2562/**
        2563 * Appends a child to a node.
        2564 * @param {Node} parent Parent.
        2565 * @param {Node} child Child.
        2566 */
        2567goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;
        2568
        2569
        2570/**
        2571 * Appends a node with text or other nodes.
        2572 * @param {!Node} parent The node to append nodes to.
        2573 * @param {...goog.dom.Appendable} var_args The things to append to the node.
        2574 * If this is a Node it is appended as is.
        2575 * If this is a string then a text node is appended.
        2576 * If this is an array like object then fields 0 to length - 1 are appended.
        2577 */
        2578goog.dom.DomHelper.prototype.append = goog.dom.append;
        2579
        2580
        2581/**
        2582 * Determines if the given node can contain children, intended to be used for
        2583 * HTML generation.
        2584 *
        2585 * @param {Node} node The node to check.
        2586 * @return {boolean} Whether the node can contain children.
        2587 */
        2588goog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren;
        2589
        2590
        2591/**
        2592 * Removes all the child nodes on a DOM node.
        2593 * @param {Node} node Node to remove children from.
        2594 */
        2595goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;
        2596
        2597
        2598/**
        2599 * Inserts a new node before an existing reference node (i.e., as the previous
        2600 * sibling). If the reference node has no parent, then does nothing.
        2601 * @param {Node} newNode Node to insert.
        2602 * @param {Node} refNode Reference node to insert before.
        2603 */
        2604goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;
        2605
        2606
        2607/**
        2608 * Inserts a new node after an existing reference node (i.e., as the next
        2609 * sibling). If the reference node has no parent, then does nothing.
        2610 * @param {Node} newNode Node to insert.
        2611 * @param {Node} refNode Reference node to insert after.
        2612 */
        2613goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;
        2614
        2615
        2616/**
        2617 * Insert a child at a given index. If index is larger than the number of child
        2618 * nodes that the parent currently has, the node is inserted as the last child
        2619 * node.
        2620 * @param {Element} parent The element into which to insert the child.
        2621 * @param {Node} child The element to insert.
        2622 * @param {number} index The index at which to insert the new child node. Must
        2623 * not be negative.
        2624 */
        2625goog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt;
        2626
        2627
        2628/**
        2629 * Removes a node from its parent.
        2630 * @param {Node} node The node to remove.
        2631 * @return {Node} The node removed if removed; else, null.
        2632 */
        2633goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;
        2634
        2635
        2636/**
        2637 * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
        2638 * parent.
        2639 * @param {Node} newNode Node to insert.
        2640 * @param {Node} oldNode Node to replace.
        2641 */
        2642goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;
        2643
        2644
        2645/**
        2646 * Flattens an element. That is, removes it and replace it with its children.
        2647 * @param {Element} element The element to flatten.
        2648 * @return {Element|undefined} The original element, detached from the document
        2649 * tree, sans children, or undefined if the element was already not in the
        2650 * document.
        2651 */
        2652goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;
        2653
        2654
        2655/**
        2656 * Returns an array containing just the element children of the given element.
        2657 * @param {Element} element The element whose element children we want.
        2658 * @return {!(Array|NodeList)} An array or array-like list of just the element
        2659 * children of the given element.
        2660 */
        2661goog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren;
        2662
        2663
        2664/**
        2665 * Returns the first child node that is an element.
        2666 * @param {Node} node The node to get the first child element of.
        2667 * @return {Element} The first child node of {@code node} that is an element.
        2668 */
        2669goog.dom.DomHelper.prototype.getFirstElementChild =
        2670 goog.dom.getFirstElementChild;
        2671
        2672
        2673/**
        2674 * Returns the last child node that is an element.
        2675 * @param {Node} node The node to get the last child element of.
        2676 * @return {Element} The last child node of {@code node} that is an element.
        2677 */
        2678goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;
        2679
        2680
        2681/**
        2682 * Returns the first next sibling that is an element.
        2683 * @param {Node} node The node to get the next sibling element of.
        2684 * @return {Element} The next sibling of {@code node} that is an element.
        2685 */
        2686goog.dom.DomHelper.prototype.getNextElementSibling =
        2687 goog.dom.getNextElementSibling;
        2688
        2689
        2690/**
        2691 * Returns the first previous sibling that is an element.
        2692 * @param {Node} node The node to get the previous sibling element of.
        2693 * @return {Element} The first previous sibling of {@code node} that is
        2694 * an element.
        2695 */
        2696goog.dom.DomHelper.prototype.getPreviousElementSibling =
        2697 goog.dom.getPreviousElementSibling;
        2698
        2699
        2700/**
        2701 * Returns the next node in source order from the given node.
        2702 * @param {Node} node The node.
        2703 * @return {Node} The next node in the DOM tree, or null if this was the last
        2704 * node.
        2705 */
        2706goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;
        2707
        2708
        2709/**
        2710 * Returns the previous node in source order from the given node.
        2711 * @param {Node} node The node.
        2712 * @return {Node} The previous node in the DOM tree, or null if this was the
        2713 * first node.
        2714 */
        2715goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;
        2716
        2717
        2718/**
        2719 * Whether the object looks like a DOM node.
        2720 * @param {?} obj The object being tested for node likeness.
        2721 * @return {boolean} Whether the object looks like a DOM node.
        2722 */
        2723goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;
        2724
        2725
        2726/**
        2727 * Whether the object looks like an Element.
        2728 * @param {?} obj The object being tested for Element likeness.
        2729 * @return {boolean} Whether the object looks like an Element.
        2730 */
        2731goog.dom.DomHelper.prototype.isElement = goog.dom.isElement;
        2732
        2733
        2734/**
        2735 * Returns true if the specified value is a Window object. This includes the
        2736 * global window for HTML pages, and iframe windows.
        2737 * @param {?} obj Variable to test.
        2738 * @return {boolean} Whether the variable is a window.
        2739 */
        2740goog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow;
        2741
        2742
        2743/**
        2744 * Returns an element's parent, if it's an Element.
        2745 * @param {Element} element The DOM element.
        2746 * @return {Element} The parent, or null if not an Element.
        2747 */
        2748goog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement;
        2749
        2750
        2751/**
        2752 * Whether a node contains another node.
        2753 * @param {Node} parent The node that should contain the other node.
        2754 * @param {Node} descendant The node to test presence of.
        2755 * @return {boolean} Whether the parent node contains the descendent node.
        2756 */
        2757goog.dom.DomHelper.prototype.contains = goog.dom.contains;
        2758
        2759
        2760/**
        2761 * Compares the document order of two nodes, returning 0 if they are the same
        2762 * node, a negative number if node1 is before node2, and a positive number if
        2763 * node2 is before node1. Note that we compare the order the tags appear in the
        2764 * document so in the tree <b><i>text</i></b> the B node is considered to be
        2765 * before the I node.
        2766 *
        2767 * @param {Node} node1 The first node to compare.
        2768 * @param {Node} node2 The second node to compare.
        2769 * @return {number} 0 if the nodes are the same node, a negative number if node1
        2770 * is before node2, and a positive number if node2 is before node1.
        2771 */
        2772goog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder;
        2773
        2774
        2775/**
        2776 * Find the deepest common ancestor of the given nodes.
        2777 * @param {...Node} var_args The nodes to find a common ancestor of.
        2778 * @return {Node} The common ancestor of the nodes, or null if there is none.
        2779 * null will only be returned if two or more of the nodes are from different
        2780 * documents.
        2781 */
        2782goog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor;
        2783
        2784
        2785/**
        2786 * Returns the owner document for a node.
        2787 * @param {Node} node The node to get the document for.
        2788 * @return {!Document} The document owning the node.
        2789 */
        2790goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;
        2791
        2792
        2793/**
        2794 * Cross browser function for getting the document element of an iframe.
        2795 * @param {Element} iframe Iframe element.
        2796 * @return {!Document} The frame content document.
        2797 */
        2798goog.dom.DomHelper.prototype.getFrameContentDocument =
        2799 goog.dom.getFrameContentDocument;
        2800
        2801
        2802/**
        2803 * Cross browser function for getting the window of a frame or iframe.
        2804 * @param {Element} frame Frame element.
        2805 * @return {Window} The window associated with the given frame.
        2806 */
        2807goog.dom.DomHelper.prototype.getFrameContentWindow =
        2808 goog.dom.getFrameContentWindow;
        2809
        2810
        2811/**
        2812 * Sets the text content of a node, with cross-browser support.
        2813 * @param {Node} node The node to change the text content of.
        2814 * @param {string|number} text The value that should replace the node's content.
        2815 */
        2816goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;
        2817
        2818
        2819/**
        2820 * Gets the outerHTML of a node, which islike innerHTML, except that it
        2821 * actually contains the HTML of the node itself.
        2822 * @param {Element} element The element to get the HTML of.
        2823 * @return {string} The outerHTML of the given element.
        2824 */
        2825goog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml;
        2826
        2827
        2828/**
        2829 * Finds the first descendant node that matches the filter function. This does
        2830 * a depth first search.
        2831 * @param {Node} root The root of the tree to search.
        2832 * @param {function(Node) : boolean} p The filter function.
        2833 * @return {Node|undefined} The found node or undefined if none is found.
        2834 */
        2835goog.dom.DomHelper.prototype.findNode = goog.dom.findNode;
        2836
        2837
        2838/**
        2839 * Finds all the descendant nodes that matches the filter function. This does a
        2840 * depth first search.
        2841 * @param {Node} root The root of the tree to search.
        2842 * @param {function(Node) : boolean} p The filter function.
        2843 * @return {Array<Node>} The found nodes or an empty array if none are found.
        2844 */
        2845goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;
        2846
        2847
        2848/**
        2849 * Returns true if the element has a tab index that allows it to receive
        2850 * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
        2851 * natively support keyboard focus, even if they have no tab index.
        2852 * @param {!Element} element Element to check.
        2853 * @return {boolean} Whether the element has a tab index that allows keyboard
        2854 * focus.
        2855 */
        2856goog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex;
        2857
        2858
        2859/**
        2860 * Enables or disables keyboard focus support on the element via its tab index.
        2861 * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
        2862 * (or elements that natively support keyboard focus, like form elements) can
        2863 * receive keyboard focus. See http://go/tabindex for more info.
        2864 * @param {Element} element Element whose tab index is to be changed.
        2865 * @param {boolean} enable Whether to set or remove a tab index on the element
        2866 * that supports keyboard focus.
        2867 */
        2868goog.dom.DomHelper.prototype.setFocusableTabIndex =
        2869 goog.dom.setFocusableTabIndex;
        2870
        2871
        2872/**
        2873 * Returns true if the element can be focused, i.e. it has a tab index that
        2874 * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
        2875 * that natively supports keyboard focus.
        2876 * @param {!Element} element Element to check.
        2877 * @return {boolean} Whether the element allows keyboard focus.
        2878 */
        2879goog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable;
        2880
        2881
        2882/**
        2883 * Returns the text contents of the current node, without markup. New lines are
        2884 * stripped and whitespace is collapsed, such that each character would be
        2885 * visible.
        2886 *
        2887 * In browsers that support it, innerText is used. Other browsers attempt to
        2888 * simulate it via node traversal. Line breaks are canonicalized in IE.
        2889 *
        2890 * @param {Node} node The node from which we are getting content.
        2891 * @return {string} The text content.
        2892 */
        2893goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;
        2894
        2895
        2896/**
        2897 * Returns the text length of the text contained in a node, without markup. This
        2898 * is equivalent to the selection length if the node was selected, or the number
        2899 * of cursor movements to traverse the node. Images & BRs take one space. New
        2900 * lines are ignored.
        2901 *
        2902 * @param {Node} node The node whose text content length is being calculated.
        2903 * @return {number} The length of {@code node}'s text content.
        2904 */
        2905goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;
        2906
        2907
        2908/**
        2909 * Returns the text offset of a node relative to one of its ancestors. The text
        2910 * length is the same as the length calculated by
        2911 * {@code goog.dom.getNodeTextLength}.
        2912 *
        2913 * @param {Node} node The node whose offset is being calculated.
        2914 * @param {Node=} opt_offsetParent Defaults to the node's owner document's body.
        2915 * @return {number} The text offset.
        2916 */
        2917goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;
        2918
        2919
        2920/**
        2921 * Returns the node at a given offset in a parent node. If an object is
        2922 * provided for the optional third parameter, the node and the remainder of the
        2923 * offset will stored as properties of this object.
        2924 * @param {Node} parent The parent node.
        2925 * @param {number} offset The offset into the parent node.
        2926 * @param {Object=} opt_result Object to be used to store the return value. The
        2927 * return value will be stored in the form {node: Node, remainder: number}
        2928 * if this object is provided.
        2929 * @return {Node} The node at the given offset.
        2930 */
        2931goog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset;
        2932
        2933
        2934/**
        2935 * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
        2936 * the object must have a numeric length property and an item function (which
        2937 * has type 'string' on IE for some reason).
        2938 * @param {Object} val Object to test.
        2939 * @return {boolean} Whether the object is a NodeList.
        2940 */
        2941goog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList;
        2942
        2943
        2944/**
        2945 * Walks up the DOM hierarchy returning the first ancestor that has the passed
        2946 * tag name and/or class name. If the passed element matches the specified
        2947 * criteria, the element itself is returned.
        2948 * @param {Node} element The DOM node to start with.
        2949 * @param {?(goog.dom.TagName|string)=} opt_tag The tag name to match (or
        2950 * null/undefined to match only based on class name).
        2951 * @param {?string=} opt_class The class name to match (or null/undefined to
        2952 * match only based on tag name).
        2953 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
        2954 * dom.
        2955 * @return {Element} The first ancestor that matches the passed criteria, or
        2956 * null if no match is found.
        2957 */
        2958goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass =
        2959 goog.dom.getAncestorByTagNameAndClass;
        2960
        2961
        2962/**
        2963 * Walks up the DOM hierarchy returning the first ancestor that has the passed
        2964 * class name. If the passed element matches the specified criteria, the
        2965 * element itself is returned.
        2966 * @param {Node} element The DOM node to start with.
        2967 * @param {string} class The class name to match.
        2968 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
        2969 * dom.
        2970 * @return {Element} The first ancestor that matches the passed criteria, or
        2971 * null if none match.
        2972 */
        2973goog.dom.DomHelper.prototype.getAncestorByClass =
        2974 goog.dom.getAncestorByClass;
        2975
        2976
        2977/**
        2978 * Walks up the DOM hierarchy returning the first ancestor that passes the
        2979 * matcher function.
        2980 * @param {Node} element The DOM node to start with.
        2981 * @param {function(Node) : boolean} matcher A function that returns true if the
        2982 * passed node matches the desired criteria.
        2983 * @param {boolean=} opt_includeNode If true, the node itself is included in
        2984 * the search (the first call to the matcher will pass startElement as
        2985 * the node to test).
        2986 * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
        2987 * dom.
        2988 * @return {Node} DOM node that matched the matcher, or null if there was
        2989 * no match.
        2990 */
        2991goog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor;
        \ No newline at end of file diff --git a/docs/source/lib/goog/dom/nodetype.js.src.html b/docs/source/lib/goog/dom/nodetype.js.src.html new file mode 100644 index 0000000..587e392 --- /dev/null +++ b/docs/source/lib/goog/dom/nodetype.js.src.html @@ -0,0 +1 @@ +nodetype.js

        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
        19goog.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 */
        35goog.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};
        \ No newline at end of file diff --git a/docs/source/lib/goog/dom/safe.js.src.html b/docs/source/lib/goog/dom/safe.js.src.html new file mode 100644 index 0000000..e37c7c3 --- /dev/null +++ b/docs/source/lib/goog/dom/safe.js.src.html @@ -0,0 +1 @@ +safe.js

        lib/goog/dom/safe.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 Type-safe wrappers for unsafe DOM APIs.
        17 *
        18 * This file provides type-safe wrappers for DOM APIs that can result in
        19 * cross-site scripting (XSS) vulnerabilities, if the API is supplied with
        20 * untrusted (attacker-controlled) input. Instead of plain strings, the type
        21 * safe wrappers consume values of types from the goog.html package whose
        22 * contract promises that values are safe to use in the corresponding context.
        23 *
        24 * Hence, a program that exclusively uses the wrappers in this file (i.e., whose
        25 * only reference to security-sensitive raw DOM APIs are in this file) is
        26 * guaranteed to be free of XSS due to incorrect use of such DOM APIs (modulo
        27 * correctness of code that produces values of the respective goog.html types,
        28 * and absent code that violates type safety).
        29 *
        30 * For example, assigning to an element's .innerHTML property a string that is
        31 * derived (even partially) from untrusted input typically results in an XSS
        32 * vulnerability. The type-safe wrapper goog.html.setInnerHtml consumes a value
        33 * of type goog.html.SafeHtml, whose contract states that using its values in a
        34 * HTML context will not result in XSS. Hence a program that is free of direct
        35 * assignments to any element's innerHTML property (with the exception of the
        36 * assignment to .innerHTML in this file) is guaranteed to be free of XSS due to
        37 * assignment of untrusted strings to the innerHTML property.
        38 */
        39
        40goog.provide('goog.dom.safe');
        41goog.provide('goog.dom.safe.InsertAdjacentHtmlPosition');
        42
        43goog.require('goog.asserts');
        44goog.require('goog.html.SafeHtml');
        45goog.require('goog.html.SafeUrl');
        46goog.require('goog.html.TrustedResourceUrl');
        47goog.require('goog.string');
        48goog.require('goog.string.Const');
        49
        50
        51/** @enum {string} */
        52goog.dom.safe.InsertAdjacentHtmlPosition = {
        53 AFTERBEGIN: 'afterbegin',
        54 AFTEREND: 'afterend',
        55 BEFOREBEGIN: 'beforebegin',
        56 BEFOREEND: 'beforeend'
        57};
        58
        59
        60/**
        61 * Inserts known-safe HTML into a Node, at the specified position.
        62 * @param {!Node} node The node on which to call insertAdjacentHTML.
        63 * @param {!goog.dom.safe.InsertAdjacentHtmlPosition} position Position where
        64 * to insert the HTML.
        65 * @param {!goog.html.SafeHtml} html The known-safe HTML to insert.
        66 */
        67goog.dom.safe.insertAdjacentHtml = function(node, position, html) {
        68 node.insertAdjacentHTML(position, goog.html.SafeHtml.unwrap(html));
        69};
        70
        71
        72/**
        73 * Assigns known-safe HTML to an element's innerHTML property.
        74 * @param {!Element} elem The element whose innerHTML is to be assigned to.
        75 * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
        76 */
        77goog.dom.safe.setInnerHtml = function(elem, html) {
        78 elem.innerHTML = goog.html.SafeHtml.unwrap(html);
        79};
        80
        81
        82/**
        83 * Assigns known-safe HTML to an element's outerHTML property.
        84 * @param {!Element} elem The element whose outerHTML is to be assigned to.
        85 * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
        86 */
        87goog.dom.safe.setOuterHtml = function(elem, html) {
        88 elem.outerHTML = goog.html.SafeHtml.unwrap(html);
        89};
        90
        91
        92/**
        93 * Writes known-safe HTML to a document.
        94 * @param {!Document} doc The document to be written to.
        95 * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
        96 */
        97goog.dom.safe.documentWrite = function(doc, html) {
        98 doc.write(goog.html.SafeHtml.unwrap(html));
        99};
        100
        101
        102/**
        103 * Safely assigns a URL to an anchor element's href property.
        104 *
        105 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
        106 * anchor's href property. If url is of type string however, it is first
        107 * sanitized using goog.html.SafeUrl.sanitize.
        108 *
        109 * Example usage:
        110 * goog.dom.safe.setAnchorHref(anchorEl, url);
        111 * which is a safe alternative to
        112 * anchorEl.href = url;
        113 * The latter can result in XSS vulnerabilities if url is a
        114 * user-/attacker-controlled value.
        115 *
        116 * @param {!HTMLAnchorElement} anchor The anchor element whose href property
        117 * is to be assigned to.
        118 * @param {string|!goog.html.SafeUrl} url The URL to assign.
        119 * @see goog.html.SafeUrl#sanitize
        120 */
        121goog.dom.safe.setAnchorHref = function(anchor, url) {
        122 /** @type {!goog.html.SafeUrl} */
        123 var safeUrl;
        124 if (url instanceof goog.html.SafeUrl) {
        125 safeUrl = url;
        126 } else {
        127 safeUrl = goog.html.SafeUrl.sanitize(url);
        128 }
        129 anchor.href = goog.html.SafeUrl.unwrap(safeUrl);
        130};
        131
        132
        133/**
        134 * Safely assigns a URL to an embed element's src property.
        135 *
        136 * Example usage:
        137 * goog.dom.safe.setEmbedSrc(embedEl, url);
        138 * which is a safe alternative to
        139 * embedEl.src = url;
        140 * The latter can result in loading untrusted code unless it is ensured that
        141 * the URL refers to a trustworthy resource.
        142 *
        143 * @param {!HTMLEmbedElement} embed The embed element whose src property
        144 * is to be assigned to.
        145 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
        146 */
        147goog.dom.safe.setEmbedSrc = function(embed, url) {
        148 embed.src = goog.html.TrustedResourceUrl.unwrap(url);
        149};
        150
        151
        152/**
        153 * Safely assigns a URL to a frame element's src property.
        154 *
        155 * Example usage:
        156 * goog.dom.safe.setFrameSrc(frameEl, url);
        157 * which is a safe alternative to
        158 * frameEl.src = url;
        159 * The latter can result in loading untrusted code unless it is ensured that
        160 * the URL refers to a trustworthy resource.
        161 *
        162 * @param {!HTMLFrameElement} frame The frame element whose src property
        163 * is to be assigned to.
        164 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
        165 */
        166goog.dom.safe.setFrameSrc = function(frame, url) {
        167 frame.src = goog.html.TrustedResourceUrl.unwrap(url);
        168};
        169
        170
        171/**
        172 * Safely assigns a URL to an iframe element's src property.
        173 *
        174 * Example usage:
        175 * goog.dom.safe.setIframeSrc(iframeEl, url);
        176 * which is a safe alternative to
        177 * iframeEl.src = url;
        178 * The latter can result in loading untrusted code unless it is ensured that
        179 * the URL refers to a trustworthy resource.
        180 *
        181 * @param {!HTMLIFrameElement} iframe The iframe element whose src property
        182 * is to be assigned to.
        183 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
        184 */
        185goog.dom.safe.setIframeSrc = function(iframe, url) {
        186 iframe.src = goog.html.TrustedResourceUrl.unwrap(url);
        187};
        188
        189
        190/**
        191 * Safely sets a link element's href and rel properties. Whether or not
        192 * the URL assigned to href has to be a goog.html.TrustedResourceUrl
        193 * depends on the value of the rel property. If rel contains "stylesheet"
        194 * then a TrustedResourceUrl is required.
        195 *
        196 * Example usage:
        197 * goog.dom.safe.setLinkHrefAndRel(linkEl, url, 'stylesheet');
        198 * which is a safe alternative to
        199 * linkEl.rel = 'stylesheet';
        200 * linkEl.href = url;
        201 * The latter can result in loading untrusted code unless it is ensured that
        202 * the URL refers to a trustworthy resource.
        203 *
        204 * @param {!HTMLLinkElement} link The link element whose href property
        205 * is to be assigned to.
        206 * @param {string|!goog.html.SafeUrl|!goog.html.TrustedResourceUrl} url The URL
        207 * to assign to the href property. Must be a TrustedResourceUrl if the
        208 * value assigned to rel contains "stylesheet". A string value is
        209 * sanitized with goog.html.SafeUrl.sanitize.
        210 * @param {string} rel The value to assign to the rel property.
        211 * @throws {Error} if rel contains "stylesheet" and url is not a
        212 * TrustedResourceUrl
        213 * @see goog.html.SafeUrl#sanitize
        214 */
        215goog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {
        216 link.rel = rel;
        217 if (goog.string.caseInsensitiveContains(rel, 'stylesheet')) {
        218 goog.asserts.assert(
        219 url instanceof goog.html.TrustedResourceUrl,
        220 'URL must be TrustedResourceUrl because "rel" contains "stylesheet"');
        221 link.href = goog.html.TrustedResourceUrl.unwrap(url);
        222 } else if (url instanceof goog.html.TrustedResourceUrl) {
        223 link.href = goog.html.TrustedResourceUrl.unwrap(url);
        224 } else if (url instanceof goog.html.SafeUrl) {
        225 link.href = goog.html.SafeUrl.unwrap(url);
        226 } else { // string
        227 // SafeUrl.sanitize must return legitimate SafeUrl when passed a string.
        228 link.href = goog.html.SafeUrl.sanitize(url).getTypedStringValue();
        229 }
        230};
        231
        232
        233/**
        234 * Safely assigns a URL to an object element's data property.
        235 *
        236 * Example usage:
        237 * goog.dom.safe.setObjectData(objectEl, url);
        238 * which is a safe alternative to
        239 * objectEl.data = url;
        240 * The latter can result in loading untrusted code unless setit is ensured that
        241 * the URL refers to a trustworthy resource.
        242 *
        243 * @param {!HTMLObjectElement} object The object element whose data property
        244 * is to be assigned to.
        245 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
        246 */
        247goog.dom.safe.setObjectData = function(object, url) {
        248 object.data = goog.html.TrustedResourceUrl.unwrap(url);
        249};
        250
        251
        252/**
        253 * Safely assigns a URL to an iframe element's src property.
        254 *
        255 * Example usage:
        256 * goog.dom.safe.setScriptSrc(scriptEl, url);
        257 * which is a safe alternative to
        258 * scriptEl.src = url;
        259 * The latter can result in loading untrusted code unless it is ensured that
        260 * the URL refers to a trustworthy resource.
        261 *
        262 * @param {!HTMLScriptElement} script The script element whose src property
        263 * is to be assigned to.
        264 * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
        265 */
        266goog.dom.safe.setScriptSrc = function(script, url) {
        267 script.src = goog.html.TrustedResourceUrl.unwrap(url);
        268};
        269
        270
        271/**
        272 * Safely assigns a URL to a Location object's href property.
        273 *
        274 * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
        275 * loc's href property. If url is of type string however, it is first sanitized
        276 * using goog.html.SafeUrl.sanitize.
        277 *
        278 * Example usage:
        279 * goog.dom.safe.setLocationHref(document.location, redirectUrl);
        280 * which is a safe alternative to
        281 * document.location.href = redirectUrl;
        282 * The latter can result in XSS vulnerabilities if redirectUrl is a
        283 * user-/attacker-controlled value.
        284 *
        285 * @param {!Location} loc The Location object whose href property is to be
        286 * assigned to.
        287 * @param {string|!goog.html.SafeUrl} url The URL to assign.
        288 * @see goog.html.SafeUrl#sanitize
        289 */
        290goog.dom.safe.setLocationHref = function(loc, url) {
        291 /** @type {!goog.html.SafeUrl} */
        292 var safeUrl;
        293 if (url instanceof goog.html.SafeUrl) {
        294 safeUrl = url;
        295 } else {
        296 safeUrl = goog.html.SafeUrl.sanitize(url);
        297 }
        298 loc.href = goog.html.SafeUrl.unwrap(safeUrl);
        299};
        300
        301
        302/**
        303 * Safely opens a URL in a new window (via window.open).
        304 *
        305 * If url is of type goog.html.SafeUrl, its value is unwrapped and passed in to
        306 * window.open. If url is of type string however, it is first sanitized
        307 * using goog.html.SafeUrl.sanitize.
        308 *
        309 * Note that this function does not prevent leakages via the referer that is
        310 * sent by window.open. It is advised to only use this to open 1st party URLs.
        311 *
        312 * Example usage:
        313 * goog.dom.safe.openInWindow(url);
        314 * which is a safe alternative to
        315 * window.open(url);
        316 * The latter can result in XSS vulnerabilities if redirectUrl is a
        317 * user-/attacker-controlled value.
        318 *
        319 * @param {string|!goog.html.SafeUrl} url The URL to open.
        320 * @param {Window=} opt_openerWin Window of which to call the .open() method.
        321 * Defaults to the global window.
        322 * @param {!goog.string.Const=} opt_name Name of the window to open in. Can be
        323 * _top, etc as allowed by window.open().
        324 * @param {string=} opt_specs Comma-separated list of specifications, same as
        325 * in window.open().
        326 * @param {boolean=} opt_replace Whether to replace the current entry in browser
        327 * history, same as in window.open().
        328 * @return {Window} Window the url was opened in.
        329 */
        330goog.dom.safe.openInWindow = function(
        331 url, opt_openerWin, opt_name, opt_specs, opt_replace) {
        332 /** @type {!goog.html.SafeUrl} */
        333 var safeUrl;
        334 if (url instanceof goog.html.SafeUrl) {
        335 safeUrl = url;
        336 } else {
        337 safeUrl = goog.html.SafeUrl.sanitize(url);
        338 }
        339 var win = opt_openerWin || window;
        340 return win.open(goog.html.SafeUrl.unwrap(safeUrl),
        341 // If opt_name is undefined, simply passing that in to open() causes IE to
        342 // reuse the current window instead of opening a new one. Thus we pass ''
        343 // in instead, which according to spec opens a new window. See
        344 // https://html.spec.whatwg.org/multipage/browsers.html#dom-open .
        345 opt_name ? goog.string.Const.unwrap(opt_name) : '',
        346 opt_specs, opt_replace);
        347};
        \ No newline at end of file diff --git a/docs/source/lib/goog/dom/tagname.js.src.html b/docs/source/lib/goog/dom/tagname.js.src.html new file mode 100644 index 0000000..871a3c4 --- /dev/null +++ b/docs/source/lib/goog/dom/tagname.js.src.html @@ -0,0 +1 @@ +tagname.js

        lib/goog/dom/tagname.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 Defines the goog.dom.TagName enum. This enumerates
        17 * all HTML tag names specified in either the the W3C HTML 4.01 index of
        18 * elements or the HTML5 draft specification.
        19 *
        20 * References:
        21 * http://www.w3.org/TR/html401/index/elements.html
        22 * http://dev.w3.org/html5/spec/section-index.html
        23 *
        24 */
        25goog.provide('goog.dom.TagName');
        26
        27
        28/**
        29 * Enum of all html tag names specified by the W3C HTML4.01 and HTML5
        30 * specifications.
        31 * @enum {string}
        32 */
        33goog.dom.TagName = {
        34 A: 'A',
        35 ABBR: 'ABBR',
        36 ACRONYM: 'ACRONYM',
        37 ADDRESS: 'ADDRESS',
        38 APPLET: 'APPLET',
        39 AREA: 'AREA',
        40 ARTICLE: 'ARTICLE',
        41 ASIDE: 'ASIDE',
        42 AUDIO: 'AUDIO',
        43 B: 'B',
        44 BASE: 'BASE',
        45 BASEFONT: 'BASEFONT',
        46 BDI: 'BDI',
        47 BDO: 'BDO',
        48 BIG: 'BIG',
        49 BLOCKQUOTE: 'BLOCKQUOTE',
        50 BODY: 'BODY',
        51 BR: 'BR',
        52 BUTTON: 'BUTTON',
        53 CANVAS: 'CANVAS',
        54 CAPTION: 'CAPTION',
        55 CENTER: 'CENTER',
        56 CITE: 'CITE',
        57 CODE: 'CODE',
        58 COL: 'COL',
        59 COLGROUP: 'COLGROUP',
        60 COMMAND: 'COMMAND',
        61 DATA: 'DATA',
        62 DATALIST: 'DATALIST',
        63 DD: 'DD',
        64 DEL: 'DEL',
        65 DETAILS: 'DETAILS',
        66 DFN: 'DFN',
        67 DIALOG: 'DIALOG',
        68 DIR: 'DIR',
        69 DIV: 'DIV',
        70 DL: 'DL',
        71 DT: 'DT',
        72 EM: 'EM',
        73 EMBED: 'EMBED',
        74 FIELDSET: 'FIELDSET',
        75 FIGCAPTION: 'FIGCAPTION',
        76 FIGURE: 'FIGURE',
        77 FONT: 'FONT',
        78 FOOTER: 'FOOTER',
        79 FORM: 'FORM',
        80 FRAME: 'FRAME',
        81 FRAMESET: 'FRAMESET',
        82 H1: 'H1',
        83 H2: 'H2',
        84 H3: 'H3',
        85 H4: 'H4',
        86 H5: 'H5',
        87 H6: 'H6',
        88 HEAD: 'HEAD',
        89 HEADER: 'HEADER',
        90 HGROUP: 'HGROUP',
        91 HR: 'HR',
        92 HTML: 'HTML',
        93 I: 'I',
        94 IFRAME: 'IFRAME',
        95 IMG: 'IMG',
        96 INPUT: 'INPUT',
        97 INS: 'INS',
        98 ISINDEX: 'ISINDEX',
        99 KBD: 'KBD',
        100 KEYGEN: 'KEYGEN',
        101 LABEL: 'LABEL',
        102 LEGEND: 'LEGEND',
        103 LI: 'LI',
        104 LINK: 'LINK',
        105 MAP: 'MAP',
        106 MARK: 'MARK',
        107 MATH: 'MATH',
        108 MENU: 'MENU',
        109 META: 'META',
        110 METER: 'METER',
        111 NAV: 'NAV',
        112 NOFRAMES: 'NOFRAMES',
        113 NOSCRIPT: 'NOSCRIPT',
        114 OBJECT: 'OBJECT',
        115 OL: 'OL',
        116 OPTGROUP: 'OPTGROUP',
        117 OPTION: 'OPTION',
        118 OUTPUT: 'OUTPUT',
        119 P: 'P',
        120 PARAM: 'PARAM',
        121 PRE: 'PRE',
        122 PROGRESS: 'PROGRESS',
        123 Q: 'Q',
        124 RP: 'RP',
        125 RT: 'RT',
        126 RUBY: 'RUBY',
        127 S: 'S',
        128 SAMP: 'SAMP',
        129 SCRIPT: 'SCRIPT',
        130 SECTION: 'SECTION',
        131 SELECT: 'SELECT',
        132 SMALL: 'SMALL',
        133 SOURCE: 'SOURCE',
        134 SPAN: 'SPAN',
        135 STRIKE: 'STRIKE',
        136 STRONG: 'STRONG',
        137 STYLE: 'STYLE',
        138 SUB: 'SUB',
        139 SUMMARY: 'SUMMARY',
        140 SUP: 'SUP',
        141 SVG: 'SVG',
        142 TABLE: 'TABLE',
        143 TBODY: 'TBODY',
        144 TD: 'TD',
        145 TEMPLATE: 'TEMPLATE',
        146 TEXTAREA: 'TEXTAREA',
        147 TFOOT: 'TFOOT',
        148 TH: 'TH',
        149 THEAD: 'THEAD',
        150 TIME: 'TIME',
        151 TITLE: 'TITLE',
        152 TR: 'TR',
        153 TRACK: 'TRACK',
        154 TT: 'TT',
        155 U: 'U',
        156 UL: 'UL',
        157 VAR: 'VAR',
        158 VIDEO: 'VIDEO',
        159 WBR: 'WBR'
        160};
        \ No newline at end of file diff --git a/docs/source/lib/goog/dom/tags.js.src.html b/docs/source/lib/goog/dom/tags.js.src.html new file mode 100644 index 0000000..09b1a51 --- /dev/null +++ b/docs/source/lib/goog/dom/tags.js.src.html @@ -0,0 +1 @@ +tags.js

        lib/goog/dom/tags.js

        1// Copyright 2014 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 HTML element tag names.
        17 */
        18goog.provide('goog.dom.tags');
        19
        20goog.require('goog.object');
        21
        22
        23/**
        24 * The void elements specified by
        25 * http://www.w3.org/TR/html-markup/syntax.html#void-elements.
        26 * @const @private {!Object<string, boolean>}
        27 */
        28goog.dom.tags.VOID_TAGS_ = goog.object.createSet(
        29 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
        30 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr');
        31
        32
        33/**
        34 * Checks whether the tag is void (with no contents allowed and no legal end
        35 * tag), for example 'br'.
        36 * @param {string} tagName The tag name in lower case.
        37 * @return {boolean}
        38 */
        39goog.dom.tags.isVoidTag = function(tagName) {
        40 return goog.dom.tags.VOID_TAGS_[tagName] === true;
        41};
        \ No newline at end of file diff --git a/docs/source/lib/goog/dom/vendor.js.src.html b/docs/source/lib/goog/dom/vendor.js.src.html new file mode 100644 index 0000000..d97e89d --- /dev/null +++ b/docs/source/lib/goog/dom/vendor.js.src.html @@ -0,0 +1 @@ +vendor.js

        lib/goog/dom/vendor.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 Vendor prefix getters.
        17 */
        18
        19goog.provide('goog.dom.vendor');
        20
        21goog.require('goog.string');
        22goog.require('goog.userAgent');
        23
        24
        25/**
        26 * Returns the JS vendor prefix used in CSS properties. Different vendors
        27 * use different methods of changing the case of the property names.
        28 *
        29 * @return {?string} The JS vendor prefix or null if there is none.
        30 */
        31goog.dom.vendor.getVendorJsPrefix = function() {
        32 if (goog.userAgent.WEBKIT) {
        33 return 'Webkit';
        34 } else if (goog.userAgent.GECKO) {
        35 return 'Moz';
        36 } else if (goog.userAgent.IE) {
        37 return 'ms';
        38 } else if (goog.userAgent.OPERA) {
        39 return 'O';
        40 }
        41
        42 return null;
        43};
        44
        45
        46/**
        47 * Returns the vendor prefix used in CSS properties.
        48 *
        49 * @return {?string} The vendor prefix or null if there is none.
        50 */
        51goog.dom.vendor.getVendorPrefix = function() {
        52 if (goog.userAgent.WEBKIT) {
        53 return '-webkit';
        54 } else if (goog.userAgent.GECKO) {
        55 return '-moz';
        56 } else if (goog.userAgent.IE) {
        57 return '-ms';
        58 } else if (goog.userAgent.OPERA) {
        59 return '-o';
        60 }
        61
        62 return null;
        63};
        64
        65
        66/**
        67 * @param {string} propertyName A property name.
        68 * @param {!Object=} opt_object If provided, we verify if the property exists in
        69 * the object.
        70 * @return {?string} A vendor prefixed property name, or null if it does not
        71 * exist.
        72 */
        73goog.dom.vendor.getPrefixedPropertyName = function(propertyName, opt_object) {
        74 // We first check for a non-prefixed property, if available.
        75 if (opt_object && propertyName in opt_object) {
        76 return propertyName;
        77 }
        78 var prefix = goog.dom.vendor.getVendorJsPrefix();
        79 if (prefix) {
        80 prefix = prefix.toLowerCase();
        81 var prefixedPropertyName = prefix + goog.string.toTitleCase(propertyName);
        82 return (!goog.isDef(opt_object) || prefixedPropertyName in opt_object) ?
        83 prefixedPropertyName : null;
        84 }
        85 return null;
        86};
        87
        88
        89/**
        90 * @param {string} eventType An event type.
        91 * @return {string} A lower-cased vendor prefixed event type.
        92 */
        93goog.dom.vendor.getPrefixedEventType = function(eventType) {
        94 var prefix = goog.dom.vendor.getVendorJsPrefix() || '';
        95 return (prefix + eventType).toLowerCase();
        96};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/browserevent.js.src.html b/docs/source/lib/goog/events/browserevent.js.src.html new file mode 100644 index 0000000..31bc078 --- /dev/null +++ b/docs/source/lib/goog/events/browserevent.js.src.html @@ -0,0 +1 @@ +browserevent.js

        lib/goog/events/browserevent.js

        1// Copyright 2005 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 A patched, standardized event object for browser events.
        17 *
        18 * <pre>
        19 * The patched event object contains the following members:
        20 * - type {string} Event type, e.g. 'click'
        21 * - target {Object} The element that actually triggered the event
        22 * - currentTarget {Object} The element the listener is attached to
        23 * - relatedTarget {Object} For mouseover and mouseout, the previous object
        24 * - offsetX {number} X-coordinate relative to target
        25 * - offsetY {number} Y-coordinate relative to target
        26 * - clientX {number} X-coordinate relative to viewport
        27 * - clientY {number} Y-coordinate relative to viewport
        28 * - screenX {number} X-coordinate relative to the edge of the screen
        29 * - screenY {number} Y-coordinate relative to the edge of the screen
        30 * - button {number} Mouse button. Use isButton() to test.
        31 * - keyCode {number} Key-code
        32 * - ctrlKey {boolean} Was ctrl key depressed
        33 * - altKey {boolean} Was alt key depressed
        34 * - shiftKey {boolean} Was shift key depressed
        35 * - metaKey {boolean} Was meta key depressed
        36 * - defaultPrevented {boolean} Whether the default action has been prevented
        37 * - state {Object} History state object
        38 *
        39 * NOTE: The keyCode member contains the raw browser keyCode. For normalized
        40 * key and character code use {@link goog.events.KeyHandler}.
        41 * </pre>
        42 *
        43 * @author arv@google.com (Erik Arvidsson)
        44 */
        45
        46goog.provide('goog.events.BrowserEvent');
        47goog.provide('goog.events.BrowserEvent.MouseButton');
        48
        49goog.require('goog.events.BrowserFeature');
        50goog.require('goog.events.Event');
        51goog.require('goog.events.EventType');
        52goog.require('goog.reflect');
        53goog.require('goog.userAgent');
        54
        55
        56
        57/**
        58 * Accepts a browser event object and creates a patched, cross browser event
        59 * object.
        60 * The content of this object will not be initialized if no event object is
        61 * provided. If this is the case, init() needs to be invoked separately.
        62 * @param {Event=} opt_e Browser event object.
        63 * @param {EventTarget=} opt_currentTarget Current target for event.
        64 * @constructor
        65 * @extends {goog.events.Event}
        66 */
        67goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
        68 goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');
        69
        70 /**
        71 * Target that fired the event.
        72 * @override
        73 * @type {Node}
        74 */
        75 this.target = null;
        76
        77 /**
        78 * Node that had the listener attached.
        79 * @override
        80 * @type {Node|undefined}
        81 */
        82 this.currentTarget = null;
        83
        84 /**
        85 * For mouseover and mouseout events, the related object for the event.
        86 * @type {Node}
        87 */
        88 this.relatedTarget = null;
        89
        90 /**
        91 * X-coordinate relative to target.
        92 * @type {number}
        93 */
        94 this.offsetX = 0;
        95
        96 /**
        97 * Y-coordinate relative to target.
        98 * @type {number}
        99 */
        100 this.offsetY = 0;
        101
        102 /**
        103 * X-coordinate relative to the window.
        104 * @type {number}
        105 */
        106 this.clientX = 0;
        107
        108 /**
        109 * Y-coordinate relative to the window.
        110 * @type {number}
        111 */
        112 this.clientY = 0;
        113
        114 /**
        115 * X-coordinate relative to the monitor.
        116 * @type {number}
        117 */
        118 this.screenX = 0;
        119
        120 /**
        121 * Y-coordinate relative to the monitor.
        122 * @type {number}
        123 */
        124 this.screenY = 0;
        125
        126 /**
        127 * Which mouse button was pressed.
        128 * @type {number}
        129 */
        130 this.button = 0;
        131
        132 /**
        133 * Keycode of key press.
        134 * @type {number}
        135 */
        136 this.keyCode = 0;
        137
        138 /**
        139 * Keycode of key press.
        140 * @type {number}
        141 */
        142 this.charCode = 0;
        143
        144 /**
        145 * Whether control was pressed at time of event.
        146 * @type {boolean}
        147 */
        148 this.ctrlKey = false;
        149
        150 /**
        151 * Whether alt was pressed at time of event.
        152 * @type {boolean}
        153 */
        154 this.altKey = false;
        155
        156 /**
        157 * Whether shift was pressed at time of event.
        158 * @type {boolean}
        159 */
        160 this.shiftKey = false;
        161
        162 /**
        163 * Whether the meta key was pressed at time of event.
        164 * @type {boolean}
        165 */
        166 this.metaKey = false;
        167
        168 /**
        169 * History state object, only set for PopState events where it's a copy of the
        170 * state object provided to pushState or replaceState.
        171 * @type {Object}
        172 */
        173 this.state = null;
        174
        175 /**
        176 * Whether the default platform modifier key was pressed at time of event.
        177 * (This is control for all platforms except Mac, where it's Meta.)
        178 * @type {boolean}
        179 */
        180 this.platformModifierKey = false;
        181
        182 /**
        183 * The browser event object.
        184 * @private {Event}
        185 */
        186 this.event_ = null;
        187
        188 if (opt_e) {
        189 this.init(opt_e, opt_currentTarget);
        190 }
        191};
        192goog.inherits(goog.events.BrowserEvent, goog.events.Event);
        193
        194
        195/**
        196 * Normalized button constants for the mouse.
        197 * @enum {number}
        198 */
        199goog.events.BrowserEvent.MouseButton = {
        200 LEFT: 0,
        201 MIDDLE: 1,
        202 RIGHT: 2
        203};
        204
        205
        206/**
        207 * Static data for mapping mouse buttons.
        208 * @type {!Array<number>}
        209 */
        210goog.events.BrowserEvent.IEButtonMap = [
        211 1, // LEFT
        212 4, // MIDDLE
        213 2 // RIGHT
        214];
        215
        216
        217/**
        218 * Accepts a browser event object and creates a patched, cross browser event
        219 * object.
        220 * @param {Event} e Browser event object.
        221 * @param {EventTarget=} opt_currentTarget Current target for event.
        222 */
        223goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
        224 var type = this.type = e.type;
        225
        226 /**
        227 * On touch devices use the first "changed touch" as the relevant touch.
        228 * @type {Touch}
        229 */
        230 var relevantTouch = e.changedTouches ? e.changedTouches[0] : null;
        231
        232 // TODO(nicksantos): Change this.target to type EventTarget.
        233 this.target = /** @type {Node} */ (e.target) || e.srcElement;
        234
        235 // TODO(nicksantos): Change this.currentTarget to type EventTarget.
        236 this.currentTarget = /** @type {Node} */ (opt_currentTarget);
        237
        238 var relatedTarget = /** @type {Node} */ (e.relatedTarget);
        239 if (relatedTarget) {
        240 // There's a bug in FireFox where sometimes, relatedTarget will be a
        241 // chrome element, and accessing any property of it will get a permission
        242 // denied exception. See:
        243 // https://bugzilla.mozilla.org/show_bug.cgi?id=497780
        244 if (goog.userAgent.GECKO) {
        245 if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
        246 relatedTarget = null;
        247 }
        248 }
        249 // TODO(arv): Use goog.events.EventType when it has been refactored into its
        250 // own file.
        251 } else if (type == goog.events.EventType.MOUSEOVER) {
        252 relatedTarget = e.fromElement;
        253 } else if (type == goog.events.EventType.MOUSEOUT) {
        254 relatedTarget = e.toElement;
        255 }
        256
        257 this.relatedTarget = relatedTarget;
        258
        259 if (!goog.isNull(relevantTouch)) {
        260 this.clientX = relevantTouch.clientX !== undefined ?
        261 relevantTouch.clientX : relevantTouch.pageX;
        262 this.clientY = relevantTouch.clientY !== undefined ?
        263 relevantTouch.clientY : relevantTouch.pageY;
        264 this.screenX = relevantTouch.screenX || 0;
        265 this.screenY = relevantTouch.screenY || 0;
        266 } else {
        267 // Webkit emits a lame warning whenever layerX/layerY is accessed.
        268 // http://code.google.com/p/chromium/issues/detail?id=101733
        269 this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
        270 e.offsetX : e.layerX;
        271 this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
        272 e.offsetY : e.layerY;
        273 this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
        274 this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
        275 this.screenX = e.screenX || 0;
        276 this.screenY = e.screenY || 0;
        277 }
        278
        279 this.button = e.button;
        280
        281 this.keyCode = e.keyCode || 0;
        282 this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
        283 this.ctrlKey = e.ctrlKey;
        284 this.altKey = e.altKey;
        285 this.shiftKey = e.shiftKey;
        286 this.metaKey = e.metaKey;
        287 this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
        288 this.state = e.state;
        289 this.event_ = e;
        290 if (e.defaultPrevented) {
        291 this.preventDefault();
        292 }
        293};
        294
        295
        296/**
        297 * Tests to see which button was pressed during the event. This is really only
        298 * useful in IE and Gecko browsers. And in IE, it's only useful for
        299 * mousedown/mouseup events, because click only fires for the left mouse button.
        300 *
        301 * Safari 2 only reports the left button being clicked, and uses the value '1'
        302 * instead of 0. Opera only reports a mousedown event for the middle button, and
        303 * no mouse events for the right button. Opera has default behavior for left and
        304 * middle click that can only be overridden via a configuration setting.
        305 *
        306 * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
        307 *
        308 * @param {goog.events.BrowserEvent.MouseButton} button The button
        309 * to test for.
        310 * @return {boolean} True if button was pressed.
        311 */
        312goog.events.BrowserEvent.prototype.isButton = function(button) {
        313 if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
        314 if (this.type == 'click') {
        315 return button == goog.events.BrowserEvent.MouseButton.LEFT;
        316 } else {
        317 return !!(this.event_.button &
        318 goog.events.BrowserEvent.IEButtonMap[button]);
        319 }
        320 } else {
        321 return this.event_.button == button;
        322 }
        323};
        324
        325
        326/**
        327 * Whether this has an "action"-producing mouse button.
        328 *
        329 * By definition, this includes left-click on windows/linux, and left-click
        330 * without the ctrl key on Macs.
        331 *
        332 * @return {boolean} The result.
        333 */
        334goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
        335 // Webkit does not ctrl+click to be a right-click, so we
        336 // normalize it to behave like Gecko and Opera.
        337 return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
        338 !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
        339};
        340
        341
        342/**
        343 * @override
        344 */
        345goog.events.BrowserEvent.prototype.stopPropagation = function() {
        346 goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
        347 if (this.event_.stopPropagation) {
        348 this.event_.stopPropagation();
        349 } else {
        350 this.event_.cancelBubble = true;
        351 }
        352};
        353
        354
        355/**
        356 * @override
        357 */
        358goog.events.BrowserEvent.prototype.preventDefault = function() {
        359 goog.events.BrowserEvent.superClass_.preventDefault.call(this);
        360 var be = this.event_;
        361 if (!be.preventDefault) {
        362 be.returnValue = false;
        363 if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
        364 /** @preserveTry */
        365 try {
        366 // Most keys can be prevented using returnValue. Some special keys
        367 // require setting the keyCode to -1 as well:
        368 //
        369 // In IE7:
        370 // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
        371 //
        372 // In IE8:
        373 // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
        374 //
        375 // We therefore do this for all function keys as well as when Ctrl key
        376 // is pressed.
        377 var VK_F1 = 112;
        378 var VK_F12 = 123;
        379 if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
        380 be.keyCode = -1;
        381 }
        382 } catch (ex) {
        383 // IE throws an 'access denied' exception when trying to change
        384 // keyCode in some situations (e.g. srcElement is input[type=file],
        385 // or srcElement is an anchor tag rewritten by parent's innerHTML).
        386 // Do nothing in this case.
        387 }
        388 }
        389 } else {
        390 be.preventDefault();
        391 }
        392};
        393
        394
        395/**
        396 * @return {Event} The underlying browser event object.
        397 */
        398goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
        399 return this.event_;
        400};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/browserfeature.js.src.html b/docs/source/lib/goog/events/browserfeature.js.src.html new file mode 100644 index 0000000..e7faa18 --- /dev/null +++ b/docs/source/lib/goog/events/browserfeature.js.src.html @@ -0,0 +1 @@ +browserfeature.js

        lib/goog/events/browserfeature.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 Browser capability checks for the events package.
        17 *
        18 */
        19
        20
        21goog.provide('goog.events.BrowserFeature');
        22
        23goog.require('goog.userAgent');
        24
        25
        26/**
        27 * Enum of browser capabilities.
        28 * @enum {boolean}
        29 */
        30goog.events.BrowserFeature = {
        31 /**
        32 * Whether the button attribute of the event is W3C compliant. False in
        33 * Internet Explorer prior to version 9; document-version dependent.
        34 */
        35 HAS_W3C_BUTTON: !goog.userAgent.IE ||
        36 goog.userAgent.isDocumentModeOrHigher(9),
        37
        38 /**
        39 * Whether the browser supports full W3C event model.
        40 */
        41 HAS_W3C_EVENT_SUPPORT: !goog.userAgent.IE ||
        42 goog.userAgent.isDocumentModeOrHigher(9),
        43
        44 /**
        45 * To prevent default in IE7-8 for certain keydown events we need set the
        46 * keyCode to -1.
        47 */
        48 SET_KEY_CODE_TO_PREVENT_DEFAULT: goog.userAgent.IE &&
        49 !goog.userAgent.isVersionOrHigher('9'),
        50
        51 /**
        52 * Whether the {@code navigator.onLine} property is supported.
        53 */
        54 HAS_NAVIGATOR_ONLINE_PROPERTY: !goog.userAgent.WEBKIT ||
        55 goog.userAgent.isVersionOrHigher('528'),
        56
        57 /**
        58 * Whether HTML5 network online/offline events are supported.
        59 */
        60 HAS_HTML5_NETWORK_EVENT_SUPPORT:
        61 goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9b') ||
        62 goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8') ||
        63 goog.userAgent.OPERA && goog.userAgent.isVersionOrHigher('9.5') ||
        64 goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('528'),
        65
        66 /**
        67 * Whether HTML5 network events fire on document.body, or otherwise the
        68 * window.
        69 */
        70 HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
        71 goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('8') ||
        72 goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
        73
        74 /**
        75 * Whether touch is enabled in the browser.
        76 */
        77 TOUCH_ENABLED:
        78 ('ontouchstart' in goog.global ||
        79 !!(goog.global['document'] &&
        80 document.documentElement &&
        81 'ontouchstart' in document.documentElement) ||
        82 // IE10 uses non-standard touch events, so it has a different check.
        83 !!(goog.global['navigator'] &&
        84 goog.global['navigator']['msMaxTouchPoints']))
        85};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/event.js.src.html b/docs/source/lib/goog/events/event.js.src.html new file mode 100644 index 0000000..23bb1d5 --- /dev/null +++ b/docs/source/lib/goog/events/event.js.src.html @@ -0,0 +1 @@ +event.js

        lib/goog/events/event.js

        1// Copyright 2005 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 A base class for event objects.
        17 *
        18 */
        19
        20
        21goog.provide('goog.events.Event');
        22goog.provide('goog.events.EventLike');
        23
        24/**
        25 * goog.events.Event no longer depends on goog.Disposable. Keep requiring
        26 * goog.Disposable here to not break projects which assume this dependency.
        27 * @suppress {extraRequire}
        28 */
        29goog.require('goog.Disposable');
        30goog.require('goog.events.EventId');
        31
        32
        33/**
        34 * A typedef for event like objects that are dispatchable via the
        35 * goog.events.dispatchEvent function. strings are treated as the type for a
        36 * goog.events.Event. Objects are treated as an extension of a new
        37 * goog.events.Event with the type property of the object being used as the type
        38 * of the Event.
        39 * @typedef {string|Object|goog.events.Event|goog.events.EventId}
        40 */
        41goog.events.EventLike;
        42
        43
        44
        45/**
        46 * A base class for event objects, so that they can support preventDefault and
        47 * stopPropagation.
        48 *
        49 * @param {string|!goog.events.EventId} type Event Type.
        50 * @param {Object=} opt_target Reference to the object that is the target of
        51 * this event. It has to implement the {@code EventTarget} interface
        52 * declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.
        53 * @constructor
        54 */
        55goog.events.Event = function(type, opt_target) {
        56 /**
        57 * Event type.
        58 * @type {string}
        59 */
        60 this.type = type instanceof goog.events.EventId ? String(type) : type;
        61
        62 /**
        63 * TODO(tbreisacher): The type should probably be
        64 * EventTarget|goog.events.EventTarget.
        65 *
        66 * Target of the event.
        67 * @type {Object|undefined}
        68 */
        69 this.target = opt_target;
        70
        71 /**
        72 * Object that had the listener attached.
        73 * @type {Object|undefined}
        74 */
        75 this.currentTarget = this.target;
        76
        77 /**
        78 * Whether to cancel the event in internal capture/bubble processing for IE.
        79 * @type {boolean}
        80 * @public
        81 * @suppress {underscore|visibility} Technically public, but referencing this
        82 * outside this package is strongly discouraged.
        83 */
        84 this.propagationStopped_ = false;
        85
        86 /**
        87 * Whether the default action has been prevented.
        88 * This is a property to match the W3C specification at
        89 * {@link http://www.w3.org/TR/DOM-Level-3-Events/
        90 * #events-event-type-defaultPrevented}.
        91 * Must be treated as read-only outside the class.
        92 * @type {boolean}
        93 */
        94 this.defaultPrevented = false;
        95
        96 /**
        97 * Return value for in internal capture/bubble processing for IE.
        98 * @type {boolean}
        99 * @public
        100 * @suppress {underscore|visibility} Technically public, but referencing this
        101 * outside this package is strongly discouraged.
        102 */
        103 this.returnValue_ = true;
        104};
        105
        106
        107/**
        108 * Stops event propagation.
        109 */
        110goog.events.Event.prototype.stopPropagation = function() {
        111 this.propagationStopped_ = true;
        112};
        113
        114
        115/**
        116 * Prevents the default action, for example a link redirecting to a url.
        117 */
        118goog.events.Event.prototype.preventDefault = function() {
        119 this.defaultPrevented = true;
        120 this.returnValue_ = false;
        121};
        122
        123
        124/**
        125 * Stops the propagation of the event. It is equivalent to
        126 * {@code e.stopPropagation()}, but can be used as the callback argument of
        127 * {@link goog.events.listen} without declaring another function.
        128 * @param {!goog.events.Event} e An event.
        129 */
        130goog.events.Event.stopPropagation = function(e) {
        131 e.stopPropagation();
        132};
        133
        134
        135/**
        136 * Prevents the default action. It is equivalent to
        137 * {@code e.preventDefault()}, but can be used as the callback argument of
        138 * {@link goog.events.listen} without declaring another function.
        139 * @param {!goog.events.Event} e An event.
        140 */
        141goog.events.Event.preventDefault = function(e) {
        142 e.preventDefault();
        143};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/eventid.js.src.html b/docs/source/lib/goog/events/eventid.js.src.html new file mode 100644 index 0000000..b6ed918 --- /dev/null +++ b/docs/source/lib/goog/events/eventid.js.src.html @@ -0,0 +1 @@ +eventid.js

        lib/goog/events/eventid.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
        15goog.provide('goog.events.EventId');
        16
        17
        18
        19/**
        20 * A templated class that is used when registering for events. Typical usage:
        21 * <code>
        22 * /** @type {goog.events.EventId<MyEventObj>}
        23 * var myEventId = new goog.events.EventId(
        24 * goog.events.getUniqueId(('someEvent'));
        25 *
        26 * // No need to cast or declare here since the compiler knows the correct
        27 * // type of 'evt' (MyEventObj).
        28 * something.listen(myEventId, function(evt) {});
        29 * </code>
        30 *
        31 * @param {string} eventId
        32 * @template T
        33 * @constructor
        34 * @struct
        35 * @final
        36 */
        37goog.events.EventId = function(eventId) {
        38 /** @const */ this.id = eventId;
        39};
        40
        41
        42/**
        43 * @override
        44 */
        45goog.events.EventId.prototype.toString = function() {
        46 return this.id;
        47};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/events.js.src.html b/docs/source/lib/goog/events/events.js.src.html new file mode 100644 index 0000000..37c66b6 --- /dev/null +++ b/docs/source/lib/goog/events/events.js.src.html @@ -0,0 +1 @@ +events.js

        lib/goog/events/events.js

        1// Copyright 2005 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 An event manager for both native browser event
        17 * targets and custom JavaScript event targets
        18 * ({@code goog.events.Listenable}). This provides an abstraction
        19 * over browsers' event systems.
        20 *
        21 * It also provides a simulation of W3C event model's capture phase in
        22 * Internet Explorer (IE 8 and below). Caveat: the simulation does not
        23 * interact well with listeners registered directly on the elements
        24 * (bypassing goog.events) or even with listeners registered via
        25 * goog.events in a separate JS binary. In these cases, we provide
        26 * no ordering guarantees.
        27 *
        28 * The listeners will receive a "patched" event object. Such event object
        29 * contains normalized values for certain event properties that differs in
        30 * different browsers.
        31 *
        32 * Example usage:
        33 * <pre>
        34 * goog.events.listen(myNode, 'click', function(e) { alert('woo') });
        35 * goog.events.listen(myNode, 'mouseover', mouseHandler, true);
        36 * goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);
        37 * goog.events.removeAll(myNode);
        38 * </pre>
        39 *
        40 * in IE and event object patching]
        41 * @author arv@google.com (Erik Arvidsson)
        42 *
        43 * @see ../demos/events.html
        44 * @see ../demos/event-propagation.html
        45 * @see ../demos/stopevent.html
        46 */
        47
        48// IMPLEMENTATION NOTES:
        49// goog.events stores an auxiliary data structure on each EventTarget
        50// source being listened on. This allows us to take advantage of GC,
        51// having the data structure GC'd when the EventTarget is GC'd. This
        52// GC behavior is equivalent to using W3C DOM Events directly.
        53
        54goog.provide('goog.events');
        55goog.provide('goog.events.CaptureSimulationMode');
        56goog.provide('goog.events.Key');
        57goog.provide('goog.events.ListenableType');
        58
        59goog.require('goog.asserts');
        60goog.require('goog.debug.entryPointRegistry');
        61goog.require('goog.events.BrowserEvent');
        62goog.require('goog.events.BrowserFeature');
        63goog.require('goog.events.Listenable');
        64goog.require('goog.events.ListenerMap');
        65
        66goog.forwardDeclare('goog.debug.ErrorHandler');
        67goog.forwardDeclare('goog.events.EventWrapper');
        68
        69
        70/**
        71 * @typedef {number|goog.events.ListenableKey}
        72 */
        73goog.events.Key;
        74
        75
        76/**
        77 * @typedef {EventTarget|goog.events.Listenable}
        78 */
        79goog.events.ListenableType;
        80
        81
        82/**
        83 * Property name on a native event target for the listener map
        84 * associated with the event target.
        85 * @private @const {string}
        86 */
        87goog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);
        88
        89
        90/**
        91 * String used to prepend to IE event types.
        92 * @const
        93 * @private
        94 */
        95goog.events.onString_ = 'on';
        96
        97
        98/**
        99 * Map of computed "on<eventname>" strings for IE event types. Caching
        100 * this removes an extra object allocation in goog.events.listen which
        101 * improves IE6 performance.
        102 * @const
        103 * @dict
        104 * @private
        105 */
        106goog.events.onStringMap_ = {};
        107
        108
        109/**
        110 * @enum {number} Different capture simulation mode for IE8-.
        111 */
        112goog.events.CaptureSimulationMode = {
        113 /**
        114 * Does not perform capture simulation. Will asserts in IE8- when you
        115 * add capture listeners.
        116 */
        117 OFF_AND_FAIL: 0,
        118
        119 /**
        120 * Does not perform capture simulation, silently ignore capture
        121 * listeners.
        122 */
        123 OFF_AND_SILENT: 1,
        124
        125 /**
        126 * Performs capture simulation.
        127 */
        128 ON: 2
        129};
        130
        131
        132/**
        133 * @define {number} The capture simulation mode for IE8-. By default,
        134 * this is ON.
        135 */
        136goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);
        137
        138
        139/**
        140 * Estimated count of total native listeners.
        141 * @private {number}
        142 */
        143goog.events.listenerCountEstimate_ = 0;
        144
        145
        146/**
        147 * Adds an event listener for a specific event on a native event
        148 * target (such as a DOM element) or an object that has implemented
        149 * {@link goog.events.Listenable}. A listener can only be added once
        150 * to an object and if it is added again the key for the listener is
        151 * returned. Note that if the existing listener is a one-off listener
        152 * (registered via listenOnce), it will no longer be a one-off
        153 * listener after a call to listen().
        154 *
        155 * @param {EventTarget|goog.events.Listenable} src The node to listen
        156 * to events on.
        157 * @param {string|Array<string>|
        158 * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
        159 * type Event type or array of event types.
        160 * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
        161 * listener Callback method, or an object with a handleEvent function.
        162 * WARNING: passing an Object is now softly deprecated.
        163 * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
        164 * false).
        165 * @param {T=} opt_handler Element in whose scope to call the listener.
        166 * @return {goog.events.Key} Unique key for the listener.
        167 * @template T,EVENTOBJ
        168 */
        169goog.events.listen = function(src, type, listener, opt_capt, opt_handler) {
        170 if (goog.isArray(type)) {
        171 for (var i = 0; i < type.length; i++) {
        172 goog.events.listen(src, type[i], listener, opt_capt, opt_handler);
        173 }
        174 return null;
        175 }
        176
        177 listener = goog.events.wrapListener(listener);
        178 if (goog.events.Listenable.isImplementedBy(src)) {
        179 return src.listen(
        180 /** @type {string|!goog.events.EventId} */ (type),
        181 listener, opt_capt, opt_handler);
        182 } else {
        183 return goog.events.listen_(
        184 /** @type {!EventTarget} */ (src),
        185 /** @type {string|!goog.events.EventId} */ (type),
        186 listener, /* callOnce */ false, opt_capt, opt_handler);
        187 }
        188};
        189
        190
        191/**
        192 * Adds an event listener for a specific event on a native event
        193 * target. A listener can only be added once to an object and if it
        194 * is added again the key for the listener is returned.
        195 *
        196 * Note that a one-off listener will not change an existing listener,
        197 * if any. On the other hand a normal listener will change existing
        198 * one-off listener to become a normal listener.
        199 *
        200 * @param {EventTarget} src The node to listen to events on.
        201 * @param {string|!goog.events.EventId} type Event type.
        202 * @param {!Function} listener Callback function.
        203 * @param {boolean} callOnce Whether the listener is a one-off
        204 * listener or otherwise.
        205 * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
        206 * false).
        207 * @param {Object=} opt_handler Element in whose scope to call the listener.
        208 * @return {goog.events.ListenableKey} Unique key for the listener.
        209 * @private
        210 */
        211goog.events.listen_ = function(
        212 src, type, listener, callOnce, opt_capt, opt_handler) {
        213 if (!type) {
        214 throw Error('Invalid event type');
        215 }
        216
        217 var capture = !!opt_capt;
        218 if (capture && !goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
        219 if (goog.events.CAPTURE_SIMULATION_MODE ==
        220 goog.events.CaptureSimulationMode.OFF_AND_FAIL) {
        221 goog.asserts.fail('Can not register capture listener in IE8-.');
        222 return null;
        223 } else if (goog.events.CAPTURE_SIMULATION_MODE ==
        224 goog.events.CaptureSimulationMode.OFF_AND_SILENT) {
        225 return null;
        226 }
        227 }
        228
        229 var listenerMap = goog.events.getListenerMap_(src);
        230 if (!listenerMap) {
        231 src[goog.events.LISTENER_MAP_PROP_] = listenerMap =
        232 new goog.events.ListenerMap(src);
        233 }
        234
        235 var listenerObj = listenerMap.add(
        236 type, listener, callOnce, opt_capt, opt_handler);
        237
        238 // If the listenerObj already has a proxy, it has been set up
        239 // previously. We simply return.
        240 if (listenerObj.proxy) {
        241 return listenerObj;
        242 }
        243
        244 var proxy = goog.events.getProxy();
        245 listenerObj.proxy = proxy;
        246
        247 proxy.src = src;
        248 proxy.listener = listenerObj;
        249
        250 // Attach the proxy through the browser's API
        251 if (src.addEventListener) {
        252 src.addEventListener(type.toString(), proxy, capture);
        253 } else if (src.attachEvent) {
        254 // The else if above used to be an unconditional else. It would call
        255 // exception on IE11, spoiling the day of some callers. The previous
        256 // incarnation of this code, from 2007, indicates that it replaced an
        257 // earlier still version that caused excess allocations on IE6.
        258 src.attachEvent(goog.events.getOnString_(type.toString()), proxy);
        259 } else {
        260 throw Error('addEventListener and attachEvent are unavailable.');
        261 }
        262
        263 goog.events.listenerCountEstimate_++;
        264 return listenerObj;
        265};
        266
        267
        268/**
        269 * Helper function for returning a proxy function.
        270 * @return {!Function} A new or reused function object.
        271 */
        272goog.events.getProxy = function() {
        273 var proxyCallbackFunction = goog.events.handleBrowserEvent_;
        274 // Use a local var f to prevent one allocation.
        275 var f = goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ?
        276 function(eventObject) {
        277 return proxyCallbackFunction.call(f.src, f.listener, eventObject);
        278 } :
        279 function(eventObject) {
        280 var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);
        281 // NOTE(chrishenry): In IE, we hack in a capture phase. However, if
        282 // there is inline event handler which tries to prevent default (for
        283 // example <a href="..." onclick="return false">...</a>) in a
        284 // descendant element, the prevent default will be overridden
        285 // by this listener if this listener were to return true. Hence, we
        286 // return undefined.
        287 if (!v) return v;
        288 };
        289 return f;
        290};
        291
        292
        293/**
        294 * Adds an event listener for a specific event on a native event
        295 * target (such as a DOM element) or an object that has implemented
        296 * {@link goog.events.Listenable}. After the event has fired the event
        297 * listener is removed from the target.
        298 *
        299 * If an existing listener already exists, listenOnce will do
        300 * nothing. In particular, if the listener was previously registered
        301 * via listen(), listenOnce() will not turn the listener into a
        302 * one-off listener. Similarly, if there is already an existing
        303 * one-off listener, listenOnce does not modify the listeners (it is
        304 * still a once listener).
        305 *
        306 * @param {EventTarget|goog.events.Listenable} src The node to listen
        307 * to events on.
        308 * @param {string|Array<string>|
        309 * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
        310 * type Event type or array of event types.
        311 * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
        312 * listener Callback method.
        313 * @param {boolean=} opt_capt Fire in capture phase?.
        314 * @param {T=} opt_handler Element in whose scope to call the listener.
        315 * @return {goog.events.Key} Unique key for the listener.
        316 * @template T,EVENTOBJ
        317 */
        318goog.events.listenOnce = function(src, type, listener, opt_capt, opt_handler) {
        319 if (goog.isArray(type)) {
        320 for (var i = 0; i < type.length; i++) {
        321 goog.events.listenOnce(src, type[i], listener, opt_capt, opt_handler);
        322 }
        323 return null;
        324 }
        325
        326 listener = goog.events.wrapListener(listener);
        327 if (goog.events.Listenable.isImplementedBy(src)) {
        328 return src.listenOnce(
        329 /** @type {string|!goog.events.EventId} */ (type),
        330 listener, opt_capt, opt_handler);
        331 } else {
        332 return goog.events.listen_(
        333 /** @type {!EventTarget} */ (src),
        334 /** @type {string|!goog.events.EventId} */ (type),
        335 listener, /* callOnce */ true, opt_capt, opt_handler);
        336 }
        337};
        338
        339
        340/**
        341 * Adds an event listener with a specific event wrapper on a DOM Node or an
        342 * object that has implemented {@link goog.events.Listenable}. A listener can
        343 * only be added once to an object.
        344 *
        345 * @param {EventTarget|goog.events.Listenable} src The target to
        346 * listen to events on.
        347 * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
        348 * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener
        349 * Callback method, or an object with a handleEvent function.
        350 * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
        351 * false).
        352 * @param {T=} opt_handler Element in whose scope to call the listener.
        353 * @template T
        354 */
        355goog.events.listenWithWrapper = function(src, wrapper, listener, opt_capt,
        356 opt_handler) {
        357 wrapper.listen(src, listener, opt_capt, opt_handler);
        358};
        359
        360
        361/**
        362 * Removes an event listener which was added with listen().
        363 *
        364 * @param {EventTarget|goog.events.Listenable} src The target to stop
        365 * listening to events on.
        366 * @param {string|Array<string>|
        367 * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
        368 * type Event type or array of event types to unlisten to.
        369 * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
        370 * listener function to remove.
        371 * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
        372 * whether the listener is fired during the capture or bubble phase of the
        373 * event.
        374 * @param {Object=} opt_handler Element in whose scope to call the listener.
        375 * @return {?boolean} indicating whether the listener was there to remove.
        376 * @template EVENTOBJ
        377 */
        378goog.events.unlisten = function(src, type, listener, opt_capt, opt_handler) {
        379 if (goog.isArray(type)) {
        380 for (var i = 0; i < type.length; i++) {
        381 goog.events.unlisten(src, type[i], listener, opt_capt, opt_handler);
        382 }
        383 return null;
        384 }
        385
        386 listener = goog.events.wrapListener(listener);
        387 if (goog.events.Listenable.isImplementedBy(src)) {
        388 return src.unlisten(
        389 /** @type {string|!goog.events.EventId} */ (type),
        390 listener, opt_capt, opt_handler);
        391 }
        392
        393 if (!src) {
        394 // TODO(chrishenry): We should tighten the API to only accept
        395 // non-null objects, or add an assertion here.
        396 return false;
        397 }
        398
        399 var capture = !!opt_capt;
        400 var listenerMap = goog.events.getListenerMap_(
        401 /** @type {!EventTarget} */ (src));
        402 if (listenerMap) {
        403 var listenerObj = listenerMap.getListener(
        404 /** @type {string|!goog.events.EventId} */ (type),
        405 listener, capture, opt_handler);
        406 if (listenerObj) {
        407 return goog.events.unlistenByKey(listenerObj);
        408 }
        409 }
        410
        411 return false;
        412};
        413
        414
        415/**
        416 * Removes an event listener which was added with listen() by the key
        417 * returned by listen().
        418 *
        419 * @param {goog.events.Key} key The key returned by listen() for this
        420 * event listener.
        421 * @return {boolean} indicating whether the listener was there to remove.
        422 */
        423goog.events.unlistenByKey = function(key) {
        424 // TODO(chrishenry): Remove this check when tests that rely on this
        425 // are fixed.
        426 if (goog.isNumber(key)) {
        427 return false;
        428 }
        429
        430 var listener = key;
        431 if (!listener || listener.removed) {
        432 return false;
        433 }
        434
        435 var src = listener.src;
        436 if (goog.events.Listenable.isImplementedBy(src)) {
        437 return src.unlistenByKey(listener);
        438 }
        439
        440 var type = listener.type;
        441 var proxy = listener.proxy;
        442 if (src.removeEventListener) {
        443 src.removeEventListener(type, proxy, listener.capture);
        444 } else if (src.detachEvent) {
        445 src.detachEvent(goog.events.getOnString_(type), proxy);
        446 }
        447 goog.events.listenerCountEstimate_--;
        448
        449 var listenerMap = goog.events.getListenerMap_(
        450 /** @type {!EventTarget} */ (src));
        451 // TODO(chrishenry): Try to remove this conditional and execute the
        452 // first branch always. This should be safe.
        453 if (listenerMap) {
        454 listenerMap.removeByKey(listener);
        455 if (listenerMap.getTypeCount() == 0) {
        456 // Null the src, just because this is simple to do (and useful
        457 // for IE <= 7).
        458 listenerMap.src = null;
        459 // We don't use delete here because IE does not allow delete
        460 // on a window object.
        461 src[goog.events.LISTENER_MAP_PROP_] = null;
        462 }
        463 } else {
        464 listener.markAsRemoved();
        465 }
        466
        467 return true;
        468};
        469
        470
        471/**
        472 * Removes an event listener which was added with listenWithWrapper().
        473 *
        474 * @param {EventTarget|goog.events.Listenable} src The target to stop
        475 * listening to events on.
        476 * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
        477 * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
        478 * listener function to remove.
        479 * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
        480 * whether the listener is fired during the capture or bubble phase of the
        481 * event.
        482 * @param {Object=} opt_handler Element in whose scope to call the listener.
        483 */
        484goog.events.unlistenWithWrapper = function(src, wrapper, listener, opt_capt,
        485 opt_handler) {
        486 wrapper.unlisten(src, listener, opt_capt, opt_handler);
        487};
        488
        489
        490/**
        491 * Removes all listeners from an object. You can also optionally
        492 * remove listeners of a particular type.
        493 *
        494 * @param {Object|undefined} obj Object to remove listeners from. Must be an
        495 * EventTarget or a goog.events.Listenable.
        496 * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
        497 * Default is all types.
        498 * @return {number} Number of listeners removed.
        499 */
        500goog.events.removeAll = function(obj, opt_type) {
        501 // TODO(chrishenry): Change the type of obj to
        502 // (!EventTarget|!goog.events.Listenable).
        503
        504 if (!obj) {
        505 return 0;
        506 }
        507
        508 if (goog.events.Listenable.isImplementedBy(obj)) {
        509 return obj.removeAllListeners(opt_type);
        510 }
        511
        512 var listenerMap = goog.events.getListenerMap_(
        513 /** @type {!EventTarget} */ (obj));
        514 if (!listenerMap) {
        515 return 0;
        516 }
        517
        518 var count = 0;
        519 var typeStr = opt_type && opt_type.toString();
        520 for (var type in listenerMap.listeners) {
        521 if (!typeStr || type == typeStr) {
        522 // Clone so that we don't need to worry about unlistenByKey
        523 // changing the content of the ListenerMap.
        524 var listeners = listenerMap.listeners[type].concat();
        525 for (var i = 0; i < listeners.length; ++i) {
        526 if (goog.events.unlistenByKey(listeners[i])) {
        527 ++count;
        528 }
        529 }
        530 }
        531 }
        532 return count;
        533};
        534
        535
        536/**
        537 * Gets the listeners for a given object, type and capture phase.
        538 *
        539 * @param {Object} obj Object to get listeners for.
        540 * @param {string|!goog.events.EventId} type Event type.
        541 * @param {boolean} capture Capture phase?.
        542 * @return {Array<goog.events.Listener>} Array of listener objects.
        543 */
        544goog.events.getListeners = function(obj, type, capture) {
        545 if (goog.events.Listenable.isImplementedBy(obj)) {
        546 return obj.getListeners(type, capture);
        547 } else {
        548 if (!obj) {
        549 // TODO(chrishenry): We should tighten the API to accept
        550 // !EventTarget|goog.events.Listenable, and add an assertion here.
        551 return [];
        552 }
        553
        554 var listenerMap = goog.events.getListenerMap_(
        555 /** @type {!EventTarget} */ (obj));
        556 return listenerMap ? listenerMap.getListeners(type, capture) : [];
        557 }
        558};
        559
        560
        561/**
        562 * Gets the goog.events.Listener for the event or null if no such listener is
        563 * in use.
        564 *
        565 * @param {EventTarget|goog.events.Listenable} src The target from
        566 * which to get listeners.
        567 * @param {?string|!goog.events.EventId<EVENTOBJ>} type The type of the event.
        568 * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The
        569 * listener function to get.
        570 * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
        571 * whether the listener is fired during the
        572 * capture or bubble phase of the event.
        573 * @param {Object=} opt_handler Element in whose scope to call the listener.
        574 * @return {goog.events.ListenableKey} the found listener or null if not found.
        575 * @template EVENTOBJ
        576 */
        577goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {
        578 // TODO(chrishenry): Change type from ?string to string, or add assertion.
        579 type = /** @type {string} */ (type);
        580 listener = goog.events.wrapListener(listener);
        581 var capture = !!opt_capt;
        582 if (goog.events.Listenable.isImplementedBy(src)) {
        583 return src.getListener(type, listener, capture, opt_handler);
        584 }
        585
        586 if (!src) {
        587 // TODO(chrishenry): We should tighten the API to only accept
        588 // non-null objects, or add an assertion here.
        589 return null;
        590 }
        591
        592 var listenerMap = goog.events.getListenerMap_(
        593 /** @type {!EventTarget} */ (src));
        594 if (listenerMap) {
        595 return listenerMap.getListener(type, listener, capture, opt_handler);
        596 }
        597 return null;
        598};
        599
        600
        601/**
        602 * Returns whether an event target has any active listeners matching the
        603 * specified signature. If either the type or capture parameters are
        604 * unspecified, the function will match on the remaining criteria.
        605 *
        606 * @param {EventTarget|goog.events.Listenable} obj Target to get
        607 * listeners for.
        608 * @param {string|!goog.events.EventId=} opt_type Event type.
        609 * @param {boolean=} opt_capture Whether to check for capture or bubble-phase
        610 * listeners.
        611 * @return {boolean} Whether an event target has one or more listeners matching
        612 * the requested type and/or capture phase.
        613 */
        614goog.events.hasListener = function(obj, opt_type, opt_capture) {
        615 if (goog.events.Listenable.isImplementedBy(obj)) {
        616 return obj.hasListener(opt_type, opt_capture);
        617 }
        618
        619 var listenerMap = goog.events.getListenerMap_(
        620 /** @type {!EventTarget} */ (obj));
        621 return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);
        622};
        623
        624
        625/**
        626 * Provides a nice string showing the normalized event objects public members
        627 * @param {Object} e Event Object.
        628 * @return {string} String of the public members of the normalized event object.
        629 */
        630goog.events.expose = function(e) {
        631 var str = [];
        632 for (var key in e) {
        633 if (e[key] && e[key].id) {
        634 str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');
        635 } else {
        636 str.push(key + ' = ' + e[key]);
        637 }
        638 }
        639 return str.join('\n');
        640};
        641
        642
        643/**
        644 * Returns a string with on prepended to the specified type. This is used for IE
        645 * which expects "on" to be prepended. This function caches the string in order
        646 * to avoid extra allocations in steady state.
        647 * @param {string} type Event type.
        648 * @return {string} The type string with 'on' prepended.
        649 * @private
        650 */
        651goog.events.getOnString_ = function(type) {
        652 if (type in goog.events.onStringMap_) {
        653 return goog.events.onStringMap_[type];
        654 }
        655 return goog.events.onStringMap_[type] = goog.events.onString_ + type;
        656};
        657
        658
        659/**
        660 * Fires an object's listeners of a particular type and phase
        661 *
        662 * @param {Object} obj Object whose listeners to call.
        663 * @param {string|!goog.events.EventId} type Event type.
        664 * @param {boolean} capture Which event phase.
        665 * @param {Object} eventObject Event object to be passed to listener.
        666 * @return {boolean} True if all listeners returned true else false.
        667 */
        668goog.events.fireListeners = function(obj, type, capture, eventObject) {
        669 if (goog.events.Listenable.isImplementedBy(obj)) {
        670 return obj.fireListeners(type, capture, eventObject);
        671 }
        672
        673 return goog.events.fireListeners_(obj, type, capture, eventObject);
        674};
        675
        676
        677/**
        678 * Fires an object's listeners of a particular type and phase.
        679 * @param {Object} obj Object whose listeners to call.
        680 * @param {string|!goog.events.EventId} type Event type.
        681 * @param {boolean} capture Which event phase.
        682 * @param {Object} eventObject Event object to be passed to listener.
        683 * @return {boolean} True if all listeners returned true else false.
        684 * @private
        685 */
        686goog.events.fireListeners_ = function(obj, type, capture, eventObject) {
        687 /** @type {boolean} */
        688 var retval = true;
        689
        690 var listenerMap = goog.events.getListenerMap_(
        691 /** @type {EventTarget} */ (obj));
        692 if (listenerMap) {
        693 // TODO(chrishenry): Original code avoids array creation when there
        694 // is no listener, so we do the same. If this optimization turns
        695 // out to be not required, we can replace this with
        696 // listenerMap.getListeners(type, capture) instead, which is simpler.
        697 var listenerArray = listenerMap.listeners[type.toString()];
        698 if (listenerArray) {
        699 listenerArray = listenerArray.concat();
        700 for (var i = 0; i < listenerArray.length; i++) {
        701 var listener = listenerArray[i];
        702 // We might not have a listener if the listener was removed.
        703 if (listener && listener.capture == capture && !listener.removed) {
        704 var result = goog.events.fireListener(listener, eventObject);
        705 retval = retval && (result !== false);
        706 }
        707 }
        708 }
        709 }
        710 return retval;
        711};
        712
        713
        714/**
        715 * Fires a listener with a set of arguments
        716 *
        717 * @param {goog.events.Listener} listener The listener object to call.
        718 * @param {Object} eventObject The event object to pass to the listener.
        719 * @return {boolean} Result of listener.
        720 */
        721goog.events.fireListener = function(listener, eventObject) {
        722 var listenerFn = listener.listener;
        723 var listenerHandler = listener.handler || listener.src;
        724
        725 if (listener.callOnce) {
        726 goog.events.unlistenByKey(listener);
        727 }
        728 return listenerFn.call(listenerHandler, eventObject);
        729};
        730
        731
        732/**
        733 * Gets the total number of listeners currently in the system.
        734 * @return {number} Number of listeners.
        735 * @deprecated This returns estimated count, now that Closure no longer
        736 * stores a central listener registry. We still return an estimation
        737 * to keep existing listener-related tests passing. In the near future,
        738 * this function will be removed.
        739 */
        740goog.events.getTotalListenerCount = function() {
        741 return goog.events.listenerCountEstimate_;
        742};
        743
        744
        745/**
        746 * Dispatches an event (or event like object) and calls all listeners
        747 * listening for events of this type. The type of the event is decided by the
        748 * type property on the event object.
        749 *
        750 * If any of the listeners returns false OR calls preventDefault then this
        751 * function will return false. If one of the capture listeners calls
        752 * stopPropagation, then the bubble listeners won't fire.
        753 *
        754 * @param {goog.events.Listenable} src The event target.
        755 * @param {goog.events.EventLike} e Event object.
        756 * @return {boolean} If anyone called preventDefault on the event object (or
        757 * if any of the handlers returns false) this will also return false.
        758 * If there are no handlers, or if all handlers return true, this returns
        759 * true.
        760 */
        761goog.events.dispatchEvent = function(src, e) {
        762 goog.asserts.assert(
        763 goog.events.Listenable.isImplementedBy(src),
        764 'Can not use goog.events.dispatchEvent with ' +
        765 'non-goog.events.Listenable instance.');
        766 return src.dispatchEvent(e);
        767};
        768
        769
        770/**
        771 * Installs exception protection for the browser event entry point using the
        772 * given error handler.
        773 *
        774 * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
        775 * protect the entry point.
        776 */
        777goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
        778 goog.events.handleBrowserEvent_ = errorHandler.protectEntryPoint(
        779 goog.events.handleBrowserEvent_);
        780};
        781
        782
        783/**
        784 * Handles an event and dispatches it to the correct listeners. This
        785 * function is a proxy for the real listener the user specified.
        786 *
        787 * @param {goog.events.Listener} listener The listener object.
        788 * @param {Event=} opt_evt Optional event object that gets passed in via the
        789 * native event handlers.
        790 * @return {boolean} Result of the event handler.
        791 * @this {EventTarget} The object or Element that fired the event.
        792 * @private
        793 */
        794goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
        795 if (listener.removed) {
        796 return true;
        797 }
        798
        799 // Synthesize event propagation if the browser does not support W3C
        800 // event model.
        801 if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
        802 var ieEvent = opt_evt ||
        803 /** @type {Event} */ (goog.getObjectByName('window.event'));
        804 var evt = new goog.events.BrowserEvent(ieEvent, this);
        805 /** @type {boolean} */
        806 var retval = true;
        807
        808 if (goog.events.CAPTURE_SIMULATION_MODE ==
        809 goog.events.CaptureSimulationMode.ON) {
        810 // If we have not marked this event yet, we should perform capture
        811 // simulation.
        812 if (!goog.events.isMarkedIeEvent_(ieEvent)) {
        813 goog.events.markIeEvent_(ieEvent);
        814
        815 var ancestors = [];
        816 for (var parent = evt.currentTarget; parent;
        817 parent = parent.parentNode) {
        818 ancestors.push(parent);
        819 }
        820
        821 // Fire capture listeners.
        822 var type = listener.type;
        823 for (var i = ancestors.length - 1; !evt.propagationStopped_ && i >= 0;
        824 i--) {
        825 evt.currentTarget = ancestors[i];
        826 var result = goog.events.fireListeners_(ancestors[i], type, true, evt);
        827 retval = retval && result;
        828 }
        829
        830 // Fire bubble listeners.
        831 //
        832 // We can technically rely on IE to perform bubble event
        833 // propagation. However, it turns out that IE fires events in
        834 // opposite order of attachEvent registration, which broke
        835 // some code and tests that rely on the order. (While W3C DOM
        836 // Level 2 Events TR leaves the event ordering unspecified,
        837 // modern browsers and W3C DOM Level 3 Events Working Draft
        838 // actually specify the order as the registration order.)
        839 for (var i = 0; !evt.propagationStopped_ && i < ancestors.length; i++) {
        840 evt.currentTarget = ancestors[i];
        841 var result = goog.events.fireListeners_(ancestors[i], type, false, evt);
        842 retval = retval && result;
        843 }
        844 }
        845 } else {
        846 retval = goog.events.fireListener(listener, evt);
        847 }
        848 return retval;
        849 }
        850
        851 // Otherwise, simply fire the listener.
        852 return goog.events.fireListener(
        853 listener, new goog.events.BrowserEvent(opt_evt, this));
        854};
        855
        856
        857/**
        858 * This is used to mark the IE event object so we do not do the Closure pass
        859 * twice for a bubbling event.
        860 * @param {Event} e The IE browser event.
        861 * @private
        862 */
        863goog.events.markIeEvent_ = function(e) {
        864 // Only the keyCode and the returnValue can be changed. We use keyCode for
        865 // non keyboard events.
        866 // event.returnValue is a bit more tricky. It is undefined by default. A
        867 // boolean false prevents the default action. In a window.onbeforeunload and
        868 // the returnValue is non undefined it will be alerted. However, we will only
        869 // modify the returnValue for keyboard events. We can get a problem if non
        870 // closure events sets the keyCode or the returnValue
        871
        872 var useReturnValue = false;
        873
        874 if (e.keyCode == 0) {
        875 // We cannot change the keyCode in case that srcElement is input[type=file].
        876 // We could test that that is the case but that would allocate 3 objects.
        877 // If we use try/catch we will only allocate extra objects in the case of a
        878 // failure.
        879 /** @preserveTry */
        880 try {
        881 e.keyCode = -1;
        882 return;
        883 } catch (ex) {
        884 useReturnValue = true;
        885 }
        886 }
        887
        888 if (useReturnValue ||
        889 /** @type {boolean|undefined} */ (e.returnValue) == undefined) {
        890 e.returnValue = true;
        891 }
        892};
        893
        894
        895/**
        896 * This is used to check if an IE event has already been handled by the Closure
        897 * system so we do not do the Closure pass twice for a bubbling event.
        898 * @param {Event} e The IE browser event.
        899 * @return {boolean} True if the event object has been marked.
        900 * @private
        901 */
        902goog.events.isMarkedIeEvent_ = function(e) {
        903 return e.keyCode < 0 || e.returnValue != undefined;
        904};
        905
        906
        907/**
        908 * Counter to create unique event ids.
        909 * @private {number}
        910 */
        911goog.events.uniqueIdCounter_ = 0;
        912
        913
        914/**
        915 * Creates a unique event id.
        916 *
        917 * @param {string} identifier The identifier.
        918 * @return {string} A unique identifier.
        919 * @idGenerator
        920 */
        921goog.events.getUniqueId = function(identifier) {
        922 return identifier + '_' + goog.events.uniqueIdCounter_++;
        923};
        924
        925
        926/**
        927 * @param {EventTarget} src The source object.
        928 * @return {goog.events.ListenerMap} A listener map for the given
        929 * source object, or null if none exists.
        930 * @private
        931 */
        932goog.events.getListenerMap_ = function(src) {
        933 var listenerMap = src[goog.events.LISTENER_MAP_PROP_];
        934 // IE serializes the property as well (e.g. when serializing outer
        935 // HTML). So we must check that the value is of the correct type.
        936 return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;
        937};
        938
        939
        940/**
        941 * Expando property for listener function wrapper for Object with
        942 * handleEvent.
        943 * @private @const {string}
        944 */
        945goog.events.LISTENER_WRAPPER_PROP_ = '__closure_events_fn_' +
        946 ((Math.random() * 1e9) >>> 0);
        947
        948
        949/**
        950 * @param {Object|Function} listener The listener function or an
        951 * object that contains handleEvent method.
        952 * @return {!Function} Either the original function or a function that
        953 * calls obj.handleEvent. If the same listener is passed to this
        954 * function more than once, the same function is guaranteed to be
        955 * returned.
        956 */
        957goog.events.wrapListener = function(listener) {
        958 goog.asserts.assert(listener, 'Listener can not be null.');
        959
        960 if (goog.isFunction(listener)) {
        961 return listener;
        962 }
        963
        964 goog.asserts.assert(
        965 listener.handleEvent, 'An object listener must have handleEvent method.');
        966 if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {
        967 listener[goog.events.LISTENER_WRAPPER_PROP_] =
        968 function(e) { return listener.handleEvent(e); };
        969 }
        970 return listener[goog.events.LISTENER_WRAPPER_PROP_];
        971};
        972
        973
        974// Register the browser event handler as an entry point, so that
        975// it can be monitored for exception handling, etc.
        976goog.debug.entryPointRegistry.register(
        977 /**
        978 * @param {function(!Function): !Function} transformer The transforming
        979 * function.
        980 */
        981 function(transformer) {
        982 goog.events.handleBrowserEvent_ = transformer(
        983 goog.events.handleBrowserEvent_);
        984 });
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/eventtarget.js.src.html b/docs/source/lib/goog/events/eventtarget.js.src.html new file mode 100644 index 0000000..ea50cac --- /dev/null +++ b/docs/source/lib/goog/events/eventtarget.js.src.html @@ -0,0 +1 @@ +eventtarget.js

        lib/goog/events/eventtarget.js

        1// Copyright 2005 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 A disposable implementation of a custom
        17 * listenable/event target. See also: documentation for
        18 * {@code goog.events.Listenable}.
        19 *
        20 * @author arv@google.com (Erik Arvidsson) [Original implementation]
        21 * @see ../demos/eventtarget.html
        22 * @see goog.events.Listenable
        23 */
        24
        25goog.provide('goog.events.EventTarget');
        26
        27goog.require('goog.Disposable');
        28goog.require('goog.asserts');
        29goog.require('goog.events');
        30goog.require('goog.events.Event');
        31goog.require('goog.events.Listenable');
        32goog.require('goog.events.ListenerMap');
        33goog.require('goog.object');
        34
        35
        36
        37/**
        38 * An implementation of {@code goog.events.Listenable} with full W3C
        39 * EventTarget-like support (capture/bubble mechanism, stopping event
        40 * propagation, preventing default actions).
        41 *
        42 * You may subclass this class to turn your class into a Listenable.
        43 *
        44 * Unless propagation is stopped, an event dispatched by an
        45 * EventTarget will bubble to the parent returned by
        46 * {@code getParentEventTarget}. To set the parent, call
        47 * {@code setParentEventTarget}. Subclasses that don't support
        48 * changing the parent can override the setter to throw an error.
        49 *
        50 * Example usage:
        51 * <pre>
        52 * var source = new goog.events.EventTarget();
        53 * function handleEvent(e) {
        54 * alert('Type: ' + e.type + '; Target: ' + e.target);
        55 * }
        56 * source.listen('foo', handleEvent);
        57 * // Or: goog.events.listen(source, 'foo', handleEvent);
        58 * ...
        59 * source.dispatchEvent('foo'); // will call handleEvent
        60 * ...
        61 * source.unlisten('foo', handleEvent);
        62 * // Or: goog.events.unlisten(source, 'foo', handleEvent);
        63 * </pre>
        64 *
        65 * @constructor
        66 * @extends {goog.Disposable}
        67 * @implements {goog.events.Listenable}
        68 */
        69goog.events.EventTarget = function() {
        70 goog.Disposable.call(this);
        71
        72 /**
        73 * Maps of event type to an array of listeners.
        74 * @private {!goog.events.ListenerMap}
        75 */
        76 this.eventTargetListeners_ = new goog.events.ListenerMap(this);
        77
        78 /**
        79 * The object to use for event.target. Useful when mixing in an
        80 * EventTarget to another object.
        81 * @private {!Object}
        82 */
        83 this.actualEventTarget_ = this;
        84
        85 /**
        86 * Parent event target, used during event bubbling.
        87 *
        88 * TODO(chrishenry): Change this to goog.events.Listenable. This
        89 * currently breaks people who expect getParentEventTarget to return
        90 * goog.events.EventTarget.
        91 *
        92 * @private {goog.events.EventTarget}
        93 */
        94 this.parentEventTarget_ = null;
        95};
        96goog.inherits(goog.events.EventTarget, goog.Disposable);
        97goog.events.Listenable.addImplementation(goog.events.EventTarget);
        98
        99
        100/**
        101 * An artificial cap on the number of ancestors you can have. This is mainly
        102 * for loop detection.
        103 * @const {number}
        104 * @private
        105 */
        106goog.events.EventTarget.MAX_ANCESTORS_ = 1000;
        107
        108
        109/**
        110 * Returns the parent of this event target to use for bubbling.
        111 *
        112 * @return {goog.events.EventTarget} The parent EventTarget or null if
        113 * there is no parent.
        114 * @override
        115 */
        116goog.events.EventTarget.prototype.getParentEventTarget = function() {
        117 return this.parentEventTarget_;
        118};
        119
        120
        121/**
        122 * Sets the parent of this event target to use for capture/bubble
        123 * mechanism.
        124 * @param {goog.events.EventTarget} parent Parent listenable (null if none).
        125 */
        126goog.events.EventTarget.prototype.setParentEventTarget = function(parent) {
        127 this.parentEventTarget_ = parent;
        128};
        129
        130
        131/**
        132 * Adds an event listener to the event target. The same handler can only be
        133 * added once per the type. Even if you add the same handler multiple times
        134 * using the same type then it will only be called once when the event is
        135 * dispatched.
        136 *
        137 * @param {string} type The type of the event to listen for.
        138 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
        139 * to handle the event. The handler can also be an object that implements
        140 * the handleEvent method which takes the event object as argument.
        141 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
        142 * whether the listener is fired during the capture or bubble phase
        143 * of the event.
        144 * @param {Object=} opt_handlerScope Object in whose scope to call
        145 * the listener.
        146 * @deprecated Use {@code #listen} instead, when possible. Otherwise, use
        147 * {@code goog.events.listen} if you are passing Object
        148 * (instead of Function) as handler.
        149 */
        150goog.events.EventTarget.prototype.addEventListener = function(
        151 type, handler, opt_capture, opt_handlerScope) {
        152 goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);
        153};
        154
        155
        156/**
        157 * Removes an event listener from the event target. The handler must be the
        158 * same object as the one added. If the handler has not been added then
        159 * nothing is done.
        160 *
        161 * @param {string} type The type of the event to listen for.
        162 * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
        163 * to handle the event. The handler can also be an object that implements
        164 * the handleEvent method which takes the event object as argument.
        165 * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
        166 * whether the listener is fired during the capture or bubble phase
        167 * of the event.
        168 * @param {Object=} opt_handlerScope Object in whose scope to call
        169 * the listener.
        170 * @deprecated Use {@code #unlisten} instead, when possible. Otherwise, use
        171 * {@code goog.events.unlisten} if you are passing Object
        172 * (instead of Function) as handler.
        173 */
        174goog.events.EventTarget.prototype.removeEventListener = function(
        175 type, handler, opt_capture, opt_handlerScope) {
        176 goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);
        177};
        178
        179
        180/** @override */
        181goog.events.EventTarget.prototype.dispatchEvent = function(e) {
        182 this.assertInitialized_();
        183
        184 var ancestorsTree, ancestor = this.getParentEventTarget();
        185 if (ancestor) {
        186 ancestorsTree = [];
        187 var ancestorCount = 1;
        188 for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
        189 ancestorsTree.push(ancestor);
        190 goog.asserts.assert(
        191 (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),
        192 'infinite loop');
        193 }
        194 }
        195
        196 return goog.events.EventTarget.dispatchEventInternal_(
        197 this.actualEventTarget_, e, ancestorsTree);
        198};
        199
        200
        201/**
        202 * Removes listeners from this object. Classes that extend EventTarget may
        203 * need to override this method in order to remove references to DOM Elements
        204 * and additional listeners.
        205 * @override
        206 */
        207goog.events.EventTarget.prototype.disposeInternal = function() {
        208 goog.events.EventTarget.superClass_.disposeInternal.call(this);
        209
        210 this.removeAllListeners();
        211 this.parentEventTarget_ = null;
        212};
        213
        214
        215/** @override */
        216goog.events.EventTarget.prototype.listen = function(
        217 type, listener, opt_useCapture, opt_listenerScope) {
        218 this.assertInitialized_();
        219 return this.eventTargetListeners_.add(
        220 String(type), listener, false /* callOnce */, opt_useCapture,
        221 opt_listenerScope);
        222};
        223
        224
        225/** @override */
        226goog.events.EventTarget.prototype.listenOnce = function(
        227 type, listener, opt_useCapture, opt_listenerScope) {
        228 return this.eventTargetListeners_.add(
        229 String(type), listener, true /* callOnce */, opt_useCapture,
        230 opt_listenerScope);
        231};
        232
        233
        234/** @override */
        235goog.events.EventTarget.prototype.unlisten = function(
        236 type, listener, opt_useCapture, opt_listenerScope) {
        237 return this.eventTargetListeners_.remove(
        238 String(type), listener, opt_useCapture, opt_listenerScope);
        239};
        240
        241
        242/** @override */
        243goog.events.EventTarget.prototype.unlistenByKey = function(key) {
        244 return this.eventTargetListeners_.removeByKey(key);
        245};
        246
        247
        248/** @override */
        249goog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {
        250 // TODO(chrishenry): Previously, removeAllListeners can be called on
        251 // uninitialized EventTarget, so we preserve that behavior. We
        252 // should remove this when usages that rely on that fact are purged.
        253 if (!this.eventTargetListeners_) {
        254 return 0;
        255 }
        256 return this.eventTargetListeners_.removeAll(opt_type);
        257};
        258
        259
        260/** @override */
        261goog.events.EventTarget.prototype.fireListeners = function(
        262 type, capture, eventObject) {
        263 // TODO(chrishenry): Original code avoids array creation when there
        264 // is no listener, so we do the same. If this optimization turns
        265 // out to be not required, we can replace this with
        266 // getListeners(type, capture) instead, which is simpler.
        267 var listenerArray = this.eventTargetListeners_.listeners[String(type)];
        268 if (!listenerArray) {
        269 return true;
        270 }
        271 listenerArray = listenerArray.concat();
        272
        273 var rv = true;
        274 for (var i = 0; i < listenerArray.length; ++i) {
        275 var listener = listenerArray[i];
        276 // We might not have a listener if the listener was removed.
        277 if (listener && !listener.removed && listener.capture == capture) {
        278 var listenerFn = listener.listener;
        279 var listenerHandler = listener.handler || listener.src;
        280
        281 if (listener.callOnce) {
        282 this.unlistenByKey(listener);
        283 }
        284 rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
        285 }
        286 }
        287
        288 return rv && eventObject.returnValue_ != false;
        289};
        290
        291
        292/** @override */
        293goog.events.EventTarget.prototype.getListeners = function(type, capture) {
        294 return this.eventTargetListeners_.getListeners(String(type), capture);
        295};
        296
        297
        298/** @override */
        299goog.events.EventTarget.prototype.getListener = function(
        300 type, listener, capture, opt_listenerScope) {
        301 return this.eventTargetListeners_.getListener(
        302 String(type), listener, capture, opt_listenerScope);
        303};
        304
        305
        306/** @override */
        307goog.events.EventTarget.prototype.hasListener = function(
        308 opt_type, opt_capture) {
        309 var id = goog.isDef(opt_type) ? String(opt_type) : undefined;
        310 return this.eventTargetListeners_.hasListener(id, opt_capture);
        311};
        312
        313
        314/**
        315 * Sets the target to be used for {@code event.target} when firing
        316 * event. Mainly used for testing. For example, see
        317 * {@code goog.testing.events.mixinListenable}.
        318 * @param {!Object} target The target.
        319 */
        320goog.events.EventTarget.prototype.setTargetForTesting = function(target) {
        321 this.actualEventTarget_ = target;
        322};
        323
        324
        325/**
        326 * Asserts that the event target instance is initialized properly.
        327 * @private
        328 */
        329goog.events.EventTarget.prototype.assertInitialized_ = function() {
        330 goog.asserts.assert(
        331 this.eventTargetListeners_,
        332 'Event target is not initialized. Did you call the superclass ' +
        333 '(goog.events.EventTarget) constructor?');
        334};
        335
        336
        337/**
        338 * Dispatches the given event on the ancestorsTree.
        339 *
        340 * @param {!Object} target The target to dispatch on.
        341 * @param {goog.events.Event|Object|string} e The event object.
        342 * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors
        343 * tree of the target, in reverse order from the closest ancestor
        344 * to the root event target. May be null if the target has no ancestor.
        345 * @return {boolean} If anyone called preventDefault on the event object (or
        346 * if any of the listeners returns false) this will also return false.
        347 * @private
        348 */
        349goog.events.EventTarget.dispatchEventInternal_ = function(
        350 target, e, opt_ancestorsTree) {
        351 var type = e.type || /** @type {string} */ (e);
        352
        353 // If accepting a string or object, create a custom event object so that
        354 // preventDefault and stopPropagation work with the event.
        355 if (goog.isString(e)) {
        356 e = new goog.events.Event(e, target);
        357 } else if (!(e instanceof goog.events.Event)) {
        358 var oldEvent = e;
        359 e = new goog.events.Event(type, target);
        360 goog.object.extend(e, oldEvent);
        361 } else {
        362 e.target = e.target || target;
        363 }
        364
        365 var rv = true, currentTarget;
        366
        367 // Executes all capture listeners on the ancestors, if any.
        368 if (opt_ancestorsTree) {
        369 for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
        370 i--) {
        371 currentTarget = e.currentTarget = opt_ancestorsTree[i];
        372 rv = currentTarget.fireListeners(type, true, e) && rv;
        373 }
        374 }
        375
        376 // Executes capture and bubble listeners on the target.
        377 if (!e.propagationStopped_) {
        378 currentTarget = e.currentTarget = target;
        379 rv = currentTarget.fireListeners(type, true, e) && rv;
        380 if (!e.propagationStopped_) {
        381 rv = currentTarget.fireListeners(type, false, e) && rv;
        382 }
        383 }
        384
        385 // Executes all bubble listeners on the ancestors, if any.
        386 if (opt_ancestorsTree) {
        387 for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
        388 currentTarget = e.currentTarget = opt_ancestorsTree[i];
        389 rv = currentTarget.fireListeners(type, false, e) && rv;
        390 }
        391 }
        392
        393 return rv;
        394};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/eventtype.js.src.html b/docs/source/lib/goog/events/eventtype.js.src.html new file mode 100644 index 0000000..52ab644 --- /dev/null +++ b/docs/source/lib/goog/events/eventtype.js.src.html @@ -0,0 +1 @@ +eventtype.js

        lib/goog/events/eventtype.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 Event Types.
        17 *
        18 * @author arv@google.com (Erik Arvidsson)
        19 */
        20
        21
        22goog.provide('goog.events.EventType');
        23
        24goog.require('goog.userAgent');
        25
        26
        27/**
        28 * Returns a prefixed event name for the current browser.
        29 * @param {string} eventName The name of the event.
        30 * @return {string} The prefixed event name.
        31 * @suppress {missingRequire|missingProvide}
        32 * @private
        33 */
        34goog.events.getVendorPrefixedName_ = function(eventName) {
        35 return goog.userAgent.WEBKIT ? 'webkit' + eventName :
        36 (goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :
        37 eventName.toLowerCase());
        38};
        39
        40
        41/**
        42 * Constants for event names.
        43 * @enum {string}
        44 */
        45goog.events.EventType = {
        46 // Mouse events
        47 CLICK: 'click',
        48 RIGHTCLICK: 'rightclick',
        49 DBLCLICK: 'dblclick',
        50 MOUSEDOWN: 'mousedown',
        51 MOUSEUP: 'mouseup',
        52 MOUSEOVER: 'mouseover',
        53 MOUSEOUT: 'mouseout',
        54 MOUSEMOVE: 'mousemove',
        55 MOUSEENTER: 'mouseenter',
        56 MOUSELEAVE: 'mouseleave',
        57 // Select start is non-standard.
        58 // See http://msdn.microsoft.com/en-us/library/ie/ms536969(v=vs.85).aspx.
        59 SELECTSTART: 'selectstart', // IE, Safari, Chrome
        60
        61 // Wheel events
        62 // http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
        63 WHEEL: 'wheel',
        64
        65 // Key events
        66 KEYPRESS: 'keypress',
        67 KEYDOWN: 'keydown',
        68 KEYUP: 'keyup',
        69
        70 // Focus
        71 BLUR: 'blur',
        72 FOCUS: 'focus',
        73 DEACTIVATE: 'deactivate', // IE only
        74 // NOTE: The following two events are not stable in cross-browser usage.
        75 // WebKit and Opera implement DOMFocusIn/Out.
        76 // IE implements focusin/out.
        77 // Gecko implements neither see bug at
        78 // https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
        79 // The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
        80 // http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
        81 // You can use FOCUS in Capture phase until implementations converge.
        82 FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
        83 FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
        84
        85 // Forms
        86 CHANGE: 'change',
        87 RESET: 'reset',
        88 SELECT: 'select',
        89 SUBMIT: 'submit',
        90 INPUT: 'input',
        91 PROPERTYCHANGE: 'propertychange', // IE only
        92
        93 // Drag and drop
        94 DRAGSTART: 'dragstart',
        95 DRAG: 'drag',
        96 DRAGENTER: 'dragenter',
        97 DRAGOVER: 'dragover',
        98 DRAGLEAVE: 'dragleave',
        99 DROP: 'drop',
        100 DRAGEND: 'dragend',
        101
        102 // Touch events
        103 // Note that other touch events exist, but we should follow the W3C list here.
        104 // http://www.w3.org/TR/touch-events/#list-of-touchevent-types
        105 TOUCHSTART: 'touchstart',
        106 TOUCHMOVE: 'touchmove',
        107 TOUCHEND: 'touchend',
        108 TOUCHCANCEL: 'touchcancel',
        109
        110 // Misc
        111 BEFOREUNLOAD: 'beforeunload',
        112 CONSOLEMESSAGE: 'consolemessage',
        113 CONTEXTMENU: 'contextmenu',
        114 DOMCONTENTLOADED: 'DOMContentLoaded',
        115 ERROR: 'error',
        116 HELP: 'help',
        117 LOAD: 'load',
        118 LOSECAPTURE: 'losecapture',
        119 ORIENTATIONCHANGE: 'orientationchange',
        120 READYSTATECHANGE: 'readystatechange',
        121 RESIZE: 'resize',
        122 SCROLL: 'scroll',
        123 UNLOAD: 'unload',
        124
        125 // HTML 5 History events
        126 // See http://www.w3.org/TR/html5/browsers.html#event-definitions-0
        127 HASHCHANGE: 'hashchange',
        128 PAGEHIDE: 'pagehide',
        129 PAGESHOW: 'pageshow',
        130 POPSTATE: 'popstate',
        131
        132 // Copy and Paste
        133 // Support is limited. Make sure it works on your favorite browser
        134 // before using.
        135 // http://www.quirksmode.org/dom/events/cutcopypaste.html
        136 COPY: 'copy',
        137 PASTE: 'paste',
        138 CUT: 'cut',
        139 BEFORECOPY: 'beforecopy',
        140 BEFORECUT: 'beforecut',
        141 BEFOREPASTE: 'beforepaste',
        142
        143 // HTML5 online/offline events.
        144 // http://www.w3.org/TR/offline-webapps/#related
        145 ONLINE: 'online',
        146 OFFLINE: 'offline',
        147
        148 // HTML 5 worker events
        149 MESSAGE: 'message',
        150 CONNECT: 'connect',
        151
        152 // CSS animation events.
        153 /** @suppress {missingRequire} */
        154 ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),
        155 /** @suppress {missingRequire} */
        156 ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),
        157 /** @suppress {missingRequire} */
        158 ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),
        159
        160 // CSS transition events. Based on the browser support described at:
        161 // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
        162 /** @suppress {missingRequire} */
        163 TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),
        164
        165 // W3C Pointer Events
        166 // http://www.w3.org/TR/pointerevents/
        167 POINTERDOWN: 'pointerdown',
        168 POINTERUP: 'pointerup',
        169 POINTERCANCEL: 'pointercancel',
        170 POINTERMOVE: 'pointermove',
        171 POINTEROVER: 'pointerover',
        172 POINTEROUT: 'pointerout',
        173 POINTERENTER: 'pointerenter',
        174 POINTERLEAVE: 'pointerleave',
        175 GOTPOINTERCAPTURE: 'gotpointercapture',
        176 LOSTPOINTERCAPTURE: 'lostpointercapture',
        177
        178 // IE specific events.
        179 // See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx
        180 // Note: these events will be supplanted in IE11.
        181 MSGESTURECHANGE: 'MSGestureChange',
        182 MSGESTUREEND: 'MSGestureEnd',
        183 MSGESTUREHOLD: 'MSGestureHold',
        184 MSGESTURESTART: 'MSGestureStart',
        185 MSGESTURETAP: 'MSGestureTap',
        186 MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
        187 MSINERTIASTART: 'MSInertiaStart',
        188 MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
        189 MSPOINTERCANCEL: 'MSPointerCancel',
        190 MSPOINTERDOWN: 'MSPointerDown',
        191 MSPOINTERENTER: 'MSPointerEnter',
        192 MSPOINTERHOVER: 'MSPointerHover',
        193 MSPOINTERLEAVE: 'MSPointerLeave',
        194 MSPOINTERMOVE: 'MSPointerMove',
        195 MSPOINTEROUT: 'MSPointerOut',
        196 MSPOINTEROVER: 'MSPointerOver',
        197 MSPOINTERUP: 'MSPointerUp',
        198
        199 // Native IMEs/input tools events.
        200 TEXT: 'text',
        201 TEXTINPUT: 'textInput',
        202 COMPOSITIONSTART: 'compositionstart',
        203 COMPOSITIONUPDATE: 'compositionupdate',
        204 COMPOSITIONEND: 'compositionend',
        205
        206 // Webview tag events
        207 // See http://developer.chrome.com/dev/apps/webview_tag.html
        208 EXIT: 'exit',
        209 LOADABORT: 'loadabort',
        210 LOADCOMMIT: 'loadcommit',
        211 LOADREDIRECT: 'loadredirect',
        212 LOADSTART: 'loadstart',
        213 LOADSTOP: 'loadstop',
        214 RESPONSIVE: 'responsive',
        215 SIZECHANGED: 'sizechanged',
        216 UNRESPONSIVE: 'unresponsive',
        217
        218 // HTML5 Page Visibility API. See details at
        219 // {@code goog.labs.dom.PageVisibilityMonitor}.
        220 VISIBILITYCHANGE: 'visibilitychange',
        221
        222 // LocalStorage event.
        223 STORAGE: 'storage',
        224
        225 // DOM Level 2 mutation events (deprecated).
        226 DOMSUBTREEMODIFIED: 'DOMSubtreeModified',
        227 DOMNODEINSERTED: 'DOMNodeInserted',
        228 DOMNODEREMOVED: 'DOMNodeRemoved',
        229 DOMNODEREMOVEDFROMDOCUMENT: 'DOMNodeRemovedFromDocument',
        230 DOMNODEINSERTEDINTODOCUMENT: 'DOMNodeInsertedIntoDocument',
        231 DOMATTRMODIFIED: 'DOMAttrModified',
        232 DOMCHARACTERDATAMODIFIED: 'DOMCharacterDataModified',
        233
        234 // Print events.
        235 BEFOREPRINT: 'beforeprint',
        236 AFTERPRINT: 'afterprint'
        237};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/keycodes.js.src.html b/docs/source/lib/goog/events/keycodes.js.src.html new file mode 100644 index 0000000..f6d6ff6 --- /dev/null +++ b/docs/source/lib/goog/events/keycodes.js.src.html @@ -0,0 +1 @@ +keycodes.js

        lib/goog/events/keycodes.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 Constant declarations for common key codes.
        17 *
        18 * @author eae@google.com (Emil A Eklund)
        19 * @see ../demos/keyhandler.html
        20 */
        21
        22goog.provide('goog.events.KeyCodes');
        23
        24goog.require('goog.userAgent');
        25
        26goog.forwardDeclare('goog.events.BrowserEvent');
        27
        28
        29/**
        30 * Key codes for common characters.
        31 *
        32 * This list is not localized and therefore some of the key codes are not
        33 * correct for non US keyboard layouts. See comments below.
        34 *
        35 * @enum {number}
        36 */
        37goog.events.KeyCodes = {
        38 WIN_KEY_FF_LINUX: 0,
        39 MAC_ENTER: 3,
        40 BACKSPACE: 8,
        41 TAB: 9,
        42 NUM_CENTER: 12, // NUMLOCK on FF/Safari Mac
        43 ENTER: 13,
        44 SHIFT: 16,
        45 CTRL: 17,
        46 ALT: 18,
        47 PAUSE: 19,
        48 CAPS_LOCK: 20,
        49 ESC: 27,
        50 SPACE: 32,
        51 PAGE_UP: 33, // also NUM_NORTH_EAST
        52 PAGE_DOWN: 34, // also NUM_SOUTH_EAST
        53 END: 35, // also NUM_SOUTH_WEST
        54 HOME: 36, // also NUM_NORTH_WEST
        55 LEFT: 37, // also NUM_WEST
        56 UP: 38, // also NUM_NORTH
        57 RIGHT: 39, // also NUM_EAST
        58 DOWN: 40, // also NUM_SOUTH
        59 PLUS_SIGN: 43, // NOT numpad plus
        60 PRINT_SCREEN: 44,
        61 INSERT: 45, // also NUM_INSERT
        62 DELETE: 46, // also NUM_DELETE
        63 ZERO: 48,
        64 ONE: 49,
        65 TWO: 50,
        66 THREE: 51,
        67 FOUR: 52,
        68 FIVE: 53,
        69 SIX: 54,
        70 SEVEN: 55,
        71 EIGHT: 56,
        72 NINE: 57,
        73 FF_SEMICOLON: 59, // Firefox (Gecko) fires this for semicolon instead of 186
        74 FF_EQUALS: 61, // Firefox (Gecko) fires this for equals instead of 187
        75 FF_DASH: 173, // Firefox (Gecko) fires this for dash instead of 189
        76 QUESTION_MARK: 63, // needs localization
        77 AT_SIGN: 64,
        78 A: 65,
        79 B: 66,
        80 C: 67,
        81 D: 68,
        82 E: 69,
        83 F: 70,
        84 G: 71,
        85 H: 72,
        86 I: 73,
        87 J: 74,
        88 K: 75,
        89 L: 76,
        90 M: 77,
        91 N: 78,
        92 O: 79,
        93 P: 80,
        94 Q: 81,
        95 R: 82,
        96 S: 83,
        97 T: 84,
        98 U: 85,
        99 V: 86,
        100 W: 87,
        101 X: 88,
        102 Y: 89,
        103 Z: 90,
        104 META: 91, // WIN_KEY_LEFT
        105 WIN_KEY_RIGHT: 92,
        106 CONTEXT_MENU: 93,
        107 NUM_ZERO: 96,
        108 NUM_ONE: 97,
        109 NUM_TWO: 98,
        110 NUM_THREE: 99,
        111 NUM_FOUR: 100,
        112 NUM_FIVE: 101,
        113 NUM_SIX: 102,
        114 NUM_SEVEN: 103,
        115 NUM_EIGHT: 104,
        116 NUM_NINE: 105,
        117 NUM_MULTIPLY: 106,
        118 NUM_PLUS: 107,
        119 NUM_MINUS: 109,
        120 NUM_PERIOD: 110,
        121 NUM_DIVISION: 111,
        122 F1: 112,
        123 F2: 113,
        124 F3: 114,
        125 F4: 115,
        126 F5: 116,
        127 F6: 117,
        128 F7: 118,
        129 F8: 119,
        130 F9: 120,
        131 F10: 121,
        132 F11: 122,
        133 F12: 123,
        134 NUMLOCK: 144,
        135 SCROLL_LOCK: 145,
        136
        137 // OS-specific media keys like volume controls and browser controls.
        138 FIRST_MEDIA_KEY: 166,
        139 LAST_MEDIA_KEY: 183,
        140
        141 SEMICOLON: 186, // needs localization
        142 DASH: 189, // needs localization
        143 EQUALS: 187, // needs localization
        144 COMMA: 188, // needs localization
        145 PERIOD: 190, // needs localization
        146 SLASH: 191, // needs localization
        147 APOSTROPHE: 192, // needs localization
        148 TILDE: 192, // needs localization
        149 SINGLE_QUOTE: 222, // needs localization
        150 OPEN_SQUARE_BRACKET: 219, // needs localization
        151 BACKSLASH: 220, // needs localization
        152 CLOSE_SQUARE_BRACKET: 221, // needs localization
        153 WIN_KEY: 224,
        154 MAC_FF_META: 224, // Firefox (Gecko) fires this for the meta key instead of 91
        155 MAC_WK_CMD_LEFT: 91, // WebKit Left Command key fired, same as META
        156 MAC_WK_CMD_RIGHT: 93, // WebKit Right Command key fired, different from META
        157 WIN_IME: 229,
        158
        159 // "Reserved for future use". Some programs (e.g. the SlingPlayer 2.4 ActiveX
        160 // control) fire this as a hacky way to disable screensavers.
        161 VK_NONAME: 252,
        162
        163 // We've seen users whose machines fire this keycode at regular one
        164 // second intervals. The common thread among these users is that
        165 // they're all using Dell Inspiron laptops, so we suspect that this
        166 // indicates a hardware/bios problem.
        167 // http://en.community.dell.com/support-forums/laptop/f/3518/p/19285957/19523128.aspx
        168 PHANTOM: 255
        169};
        170
        171
        172/**
        173 * Returns true if the event contains a text modifying key.
        174 * @param {goog.events.BrowserEvent} e A key event.
        175 * @return {boolean} Whether it's a text modifying key.
        176 */
        177goog.events.KeyCodes.isTextModifyingKeyEvent = function(e) {
        178 if (e.altKey && !e.ctrlKey ||
        179 e.metaKey ||
        180 // Function keys don't generate text
        181 e.keyCode >= goog.events.KeyCodes.F1 &&
        182 e.keyCode <= goog.events.KeyCodes.F12) {
        183 return false;
        184 }
        185
        186 // The following keys are quite harmless, even in combination with
        187 // CTRL, ALT or SHIFT.
        188 switch (e.keyCode) {
        189 case goog.events.KeyCodes.ALT:
        190 case goog.events.KeyCodes.CAPS_LOCK:
        191 case goog.events.KeyCodes.CONTEXT_MENU:
        192 case goog.events.KeyCodes.CTRL:
        193 case goog.events.KeyCodes.DOWN:
        194 case goog.events.KeyCodes.END:
        195 case goog.events.KeyCodes.ESC:
        196 case goog.events.KeyCodes.HOME:
        197 case goog.events.KeyCodes.INSERT:
        198 case goog.events.KeyCodes.LEFT:
        199 case goog.events.KeyCodes.MAC_FF_META:
        200 case goog.events.KeyCodes.META:
        201 case goog.events.KeyCodes.NUMLOCK:
        202 case goog.events.KeyCodes.NUM_CENTER:
        203 case goog.events.KeyCodes.PAGE_DOWN:
        204 case goog.events.KeyCodes.PAGE_UP:
        205 case goog.events.KeyCodes.PAUSE:
        206 case goog.events.KeyCodes.PHANTOM:
        207 case goog.events.KeyCodes.PRINT_SCREEN:
        208 case goog.events.KeyCodes.RIGHT:
        209 case goog.events.KeyCodes.SCROLL_LOCK:
        210 case goog.events.KeyCodes.SHIFT:
        211 case goog.events.KeyCodes.UP:
        212 case goog.events.KeyCodes.VK_NONAME:
        213 case goog.events.KeyCodes.WIN_KEY:
        214 case goog.events.KeyCodes.WIN_KEY_RIGHT:
        215 return false;
        216 case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
        217 return !goog.userAgent.GECKO;
        218 default:
        219 return e.keyCode < goog.events.KeyCodes.FIRST_MEDIA_KEY ||
        220 e.keyCode > goog.events.KeyCodes.LAST_MEDIA_KEY;
        221 }
        222};
        223
        224
        225/**
        226 * Returns true if the key fires a keypress event in the current browser.
        227 *
        228 * Accoridng to MSDN [1] IE only fires keypress events for the following keys:
        229 * - Letters: A - Z (uppercase and lowercase)
        230 * - Numerals: 0 - 9
        231 * - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~
        232 * - System: ESC, SPACEBAR, ENTER
        233 *
        234 * That's not entirely correct though, for instance there's no distinction
        235 * between upper and lower case letters.
        236 *
        237 * [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx)
        238 *
        239 * Safari is similar to IE, but does not fire keypress for ESC.
        240 *
        241 * Additionally, IE6 does not fire keydown or keypress events for letters when
        242 * the control or alt keys are held down and the shift key is not. IE7 does
        243 * fire keydown in these cases, though, but not keypress.
        244 *
        245 * @param {number} keyCode A key code.
        246 * @param {number=} opt_heldKeyCode Key code of a currently-held key.
        247 * @param {boolean=} opt_shiftKey Whether the shift key is held down.
        248 * @param {boolean=} opt_ctrlKey Whether the control key is held down.
        249 * @param {boolean=} opt_altKey Whether the alt key is held down.
        250 * @return {boolean} Whether it's a key that fires a keypress event.
        251 */
        252goog.events.KeyCodes.firesKeyPressEvent = function(keyCode, opt_heldKeyCode,
        253 opt_shiftKey, opt_ctrlKey, opt_altKey) {
        254 if (!goog.userAgent.IE && !goog.userAgent.EDGE &&
        255 !(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525'))) {
        256 return true;
        257 }
        258
        259 if (goog.userAgent.MAC && opt_altKey) {
        260 return goog.events.KeyCodes.isCharacterKey(keyCode);
        261 }
        262
        263 // Alt but not AltGr which is represented as Alt+Ctrl.
        264 if (opt_altKey && !opt_ctrlKey) {
        265 return false;
        266 }
        267
        268 // Saves Ctrl or Alt + key for IE and WebKit 525+, which won't fire keypress.
        269 // Non-IE browsers and WebKit prior to 525 won't get this far so no need to
        270 // check the user agent.
        271 if (goog.isNumber(opt_heldKeyCode)) {
        272 opt_heldKeyCode = goog.events.KeyCodes.normalizeKeyCode(opt_heldKeyCode);
        273 }
        274 if (!opt_shiftKey &&
        275 (opt_heldKeyCode == goog.events.KeyCodes.CTRL ||
        276 opt_heldKeyCode == goog.events.KeyCodes.ALT ||
        277 goog.userAgent.MAC &&
        278 opt_heldKeyCode == goog.events.KeyCodes.META)) {
        279 return false;
        280 }
        281
        282 // Some keys with Ctrl/Shift do not issue keypress in WEBKIT.
        283 if ((goog.userAgent.WEBKIT || goog.userAgent.EDGE) &&
        284 opt_ctrlKey && opt_shiftKey) {
        285 switch (keyCode) {
        286 case goog.events.KeyCodes.BACKSLASH:
        287 case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
        288 case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
        289 case goog.events.KeyCodes.TILDE:
        290 case goog.events.KeyCodes.SEMICOLON:
        291 case goog.events.KeyCodes.DASH:
        292 case goog.events.KeyCodes.EQUALS:
        293 case goog.events.KeyCodes.COMMA:
        294 case goog.events.KeyCodes.PERIOD:
        295 case goog.events.KeyCodes.SLASH:
        296 case goog.events.KeyCodes.APOSTROPHE:
        297 case goog.events.KeyCodes.SINGLE_QUOTE:
        298 return false;
        299 }
        300 }
        301
        302 // When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
        303 // continues to fire keydown events as the event repeats.
        304 if (goog.userAgent.IE && opt_ctrlKey && opt_heldKeyCode == keyCode) {
        305 return false;
        306 }
        307
        308 switch (keyCode) {
        309 case goog.events.KeyCodes.ENTER:
        310 return true;
        311 case goog.events.KeyCodes.ESC:
        312 return !(goog.userAgent.WEBKIT || goog.userAgent.EDGE);
        313 }
        314
        315 return goog.events.KeyCodes.isCharacterKey(keyCode);
        316};
        317
        318
        319/**
        320 * Returns true if the key produces a character.
        321 * This does not cover characters on non-US keyboards (Russian, Hebrew, etc.).
        322 *
        323 * @param {number} keyCode A key code.
        324 * @return {boolean} Whether it's a character key.
        325 */
        326goog.events.KeyCodes.isCharacterKey = function(keyCode) {
        327 if (keyCode >= goog.events.KeyCodes.ZERO &&
        328 keyCode <= goog.events.KeyCodes.NINE) {
        329 return true;
        330 }
        331
        332 if (keyCode >= goog.events.KeyCodes.NUM_ZERO &&
        333 keyCode <= goog.events.KeyCodes.NUM_MULTIPLY) {
        334 return true;
        335 }
        336
        337 if (keyCode >= goog.events.KeyCodes.A &&
        338 keyCode <= goog.events.KeyCodes.Z) {
        339 return true;
        340 }
        341
        342 // Safari sends zero key code for non-latin characters.
        343 if ((goog.userAgent.WEBKIT || goog.userAgent.EDGE) && keyCode == 0) {
        344 return true;
        345 }
        346
        347 switch (keyCode) {
        348 case goog.events.KeyCodes.SPACE:
        349 case goog.events.KeyCodes.PLUS_SIGN:
        350 case goog.events.KeyCodes.QUESTION_MARK:
        351 case goog.events.KeyCodes.AT_SIGN:
        352 case goog.events.KeyCodes.NUM_PLUS:
        353 case goog.events.KeyCodes.NUM_MINUS:
        354 case goog.events.KeyCodes.NUM_PERIOD:
        355 case goog.events.KeyCodes.NUM_DIVISION:
        356 case goog.events.KeyCodes.SEMICOLON:
        357 case goog.events.KeyCodes.FF_SEMICOLON:
        358 case goog.events.KeyCodes.DASH:
        359 case goog.events.KeyCodes.EQUALS:
        360 case goog.events.KeyCodes.FF_EQUALS:
        361 case goog.events.KeyCodes.COMMA:
        362 case goog.events.KeyCodes.PERIOD:
        363 case goog.events.KeyCodes.SLASH:
        364 case goog.events.KeyCodes.APOSTROPHE:
        365 case goog.events.KeyCodes.SINGLE_QUOTE:
        366 case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
        367 case goog.events.KeyCodes.BACKSLASH:
        368 case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
        369 return true;
        370 default:
        371 return false;
        372 }
        373};
        374
        375
        376/**
        377 * Normalizes key codes from OS/Browser-specific value to the general one.
        378 * @param {number} keyCode The native key code.
        379 * @return {number} The normalized key code.
        380 */
        381goog.events.KeyCodes.normalizeKeyCode = function(keyCode) {
        382 if (goog.userAgent.GECKO) {
        383 return goog.events.KeyCodes.normalizeGeckoKeyCode(keyCode);
        384 } else if (goog.userAgent.MAC && goog.userAgent.WEBKIT) {
        385 return goog.events.KeyCodes.normalizeMacWebKitKeyCode(keyCode);
        386 } else {
        387 return keyCode;
        388 }
        389};
        390
        391
        392/**
        393 * Normalizes key codes from their Gecko-specific value to the general one.
        394 * @param {number} keyCode The native key code.
        395 * @return {number} The normalized key code.
        396 */
        397goog.events.KeyCodes.normalizeGeckoKeyCode = function(keyCode) {
        398 switch (keyCode) {
        399 case goog.events.KeyCodes.FF_EQUALS:
        400 return goog.events.KeyCodes.EQUALS;
        401 case goog.events.KeyCodes.FF_SEMICOLON:
        402 return goog.events.KeyCodes.SEMICOLON;
        403 case goog.events.KeyCodes.FF_DASH:
        404 return goog.events.KeyCodes.DASH;
        405 case goog.events.KeyCodes.MAC_FF_META:
        406 return goog.events.KeyCodes.META;
        407 case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
        408 return goog.events.KeyCodes.WIN_KEY;
        409 default:
        410 return keyCode;
        411 }
        412};
        413
        414
        415/**
        416 * Normalizes key codes from their Mac WebKit-specific value to the general one.
        417 * @param {number} keyCode The native key code.
        418 * @return {number} The normalized key code.
        419 */
        420goog.events.KeyCodes.normalizeMacWebKitKeyCode = function(keyCode) {
        421 switch (keyCode) {
        422 case goog.events.KeyCodes.MAC_WK_CMD_RIGHT: // 93
        423 return goog.events.KeyCodes.META; // 91
        424 default:
        425 return keyCode;
        426 }
        427};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/listenable.js.src.html b/docs/source/lib/goog/events/listenable.js.src.html new file mode 100644 index 0000000..61e8c3b --- /dev/null +++ b/docs/source/lib/goog/events/listenable.js.src.html @@ -0,0 +1 @@ +listenable.js

        lib/goog/events/listenable.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 An interface for a listenable JavaScript object.
        17 * @author chrishenry@google.com (Chris Henry)
        18 */
        19
        20goog.provide('goog.events.Listenable');
        21goog.provide('goog.events.ListenableKey');
        22
        23/** @suppress {extraRequire} */
        24goog.require('goog.events.EventId');
        25
        26
        27
        28/**
        29 * A listenable interface. A listenable is an object with the ability
        30 * to dispatch/broadcast events to "event listeners" registered via
        31 * listen/listenOnce.
        32 *
        33 * The interface allows for an event propagation mechanism similar
        34 * to one offered by native browser event targets, such as
        35 * capture/bubble mechanism, stopping propagation, and preventing
        36 * default actions. Capture/bubble mechanism depends on the ancestor
        37 * tree constructed via {@code #getParentEventTarget}; this tree
        38 * must be directed acyclic graph. The meaning of default action(s)
        39 * in preventDefault is specific to a particular use case.
        40 *
        41 * Implementations that do not support capture/bubble or can not have
        42 * a parent listenable can simply not implement any ability to set the
        43 * parent listenable (and have {@code #getParentEventTarget} return
        44 * null).
        45 *
        46 * Implementation of this class can be used with or independently from
        47 * goog.events.
        48 *
        49 * Implementation must call {@code #addImplementation(implClass)}.
        50 *
        51 * @interface
        52 * @see goog.events
        53 * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
        54 */
        55goog.events.Listenable = function() {};
        56
        57
        58/**
        59 * An expando property to indicate that an object implements
        60 * goog.events.Listenable.
        61 *
        62 * See addImplementation/isImplementedBy.
        63 *
        64 * @type {string}
        65 * @const
        66 */
        67goog.events.Listenable.IMPLEMENTED_BY_PROP =
        68 'closure_listenable_' + ((Math.random() * 1e6) | 0);
        69
        70
        71/**
        72 * Marks a given class (constructor) as an implementation of
        73 * Listenable, do that we can query that fact at runtime. The class
        74 * must have already implemented the interface.
        75 * @param {!Function} cls The class constructor. The corresponding
        76 * class must have already implemented the interface.
        77 */
        78goog.events.Listenable.addImplementation = function(cls) {
        79 cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
        80};
        81
        82
        83/**
        84 * @param {Object} obj The object to check.
        85 * @return {boolean} Whether a given instance implements Listenable. The
        86 * class/superclass of the instance must call addImplementation.
        87 */
        88goog.events.Listenable.isImplementedBy = function(obj) {
        89 return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
        90};
        91
        92
        93/**
        94 * Adds an event listener. A listener can only be added once to an
        95 * object and if it is added again the key for the listener is
        96 * returned. Note that if the existing listener is a one-off listener
        97 * (registered via listenOnce), it will no longer be a one-off
        98 * listener after a call to listen().
        99 *
        100 * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
        101 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
        102 * method.
        103 * @param {boolean=} opt_useCapture Whether to fire in capture phase
        104 * (defaults to false).
        105 * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
        106 * listener.
        107 * @return {goog.events.ListenableKey} Unique key for the listener.
        108 * @template SCOPE,EVENTOBJ
        109 */
        110goog.events.Listenable.prototype.listen;
        111
        112
        113/**
        114 * Adds an event listener that is removed automatically after the
        115 * listener fired once.
        116 *
        117 * If an existing listener already exists, listenOnce will do
        118 * nothing. In particular, if the listener was previously registered
        119 * via listen(), listenOnce() will not turn the listener into a
        120 * one-off listener. Similarly, if there is already an existing
        121 * one-off listener, listenOnce does not modify the listeners (it is
        122 * still a once listener).
        123 *
        124 * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
        125 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
        126 * method.
        127 * @param {boolean=} opt_useCapture Whether to fire in capture phase
        128 * (defaults to false).
        129 * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
        130 * listener.
        131 * @return {goog.events.ListenableKey} Unique key for the listener.
        132 * @template SCOPE,EVENTOBJ
        133 */
        134goog.events.Listenable.prototype.listenOnce;
        135
        136
        137/**
        138 * Removes an event listener which was added with listen() or listenOnce().
        139 *
        140 * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
        141 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
        142 * method.
        143 * @param {boolean=} opt_useCapture Whether to fire in capture phase
        144 * (defaults to false).
        145 * @param {SCOPE=} opt_listenerScope Object in whose scope to call
        146 * the listener.
        147 * @return {boolean} Whether any listener was removed.
        148 * @template SCOPE,EVENTOBJ
        149 */
        150goog.events.Listenable.prototype.unlisten;
        151
        152
        153/**
        154 * Removes an event listener which was added with listen() by the key
        155 * returned by listen().
        156 *
        157 * @param {goog.events.ListenableKey} key The key returned by
        158 * listen() or listenOnce().
        159 * @return {boolean} Whether any listener was removed.
        160 */
        161goog.events.Listenable.prototype.unlistenByKey;
        162
        163
        164/**
        165 * Dispatches an event (or event like object) and calls all listeners
        166 * listening for events of this type. The type of the event is decided by the
        167 * type property on the event object.
        168 *
        169 * If any of the listeners returns false OR calls preventDefault then this
        170 * function will return false. If one of the capture listeners calls
        171 * stopPropagation, then the bubble listeners won't fire.
        172 *
        173 * @param {goog.events.EventLike} e Event object.
        174 * @return {boolean} If anyone called preventDefault on the event object (or
        175 * if any of the listeners returns false) this will also return false.
        176 */
        177goog.events.Listenable.prototype.dispatchEvent;
        178
        179
        180/**
        181 * Removes all listeners from this listenable. If type is specified,
        182 * it will only remove listeners of the particular type. otherwise all
        183 * registered listeners will be removed.
        184 *
        185 * @param {string=} opt_type Type of event to remove, default is to
        186 * remove all types.
        187 * @return {number} Number of listeners removed.
        188 */
        189goog.events.Listenable.prototype.removeAllListeners;
        190
        191
        192/**
        193 * Returns the parent of this event target to use for capture/bubble
        194 * mechanism.
        195 *
        196 * NOTE(chrishenry): The name reflects the original implementation of
        197 * custom event target ({@code goog.events.EventTarget}). We decided
        198 * that changing the name is not worth it.
        199 *
        200 * @return {goog.events.Listenable} The parent EventTarget or null if
        201 * there is no parent.
        202 */
        203goog.events.Listenable.prototype.getParentEventTarget;
        204
        205
        206/**
        207 * Fires all registered listeners in this listenable for the given
        208 * type and capture mode, passing them the given eventObject. This
        209 * does not perform actual capture/bubble. Only implementors of the
        210 * interface should be using this.
        211 *
        212 * @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the
        213 * listeners to fire.
        214 * @param {boolean} capture The capture mode of the listeners to fire.
        215 * @param {EVENTOBJ} eventObject The event object to fire.
        216 * @return {boolean} Whether all listeners succeeded without
        217 * attempting to prevent default behavior. If any listener returns
        218 * false or called goog.events.Event#preventDefault, this returns
        219 * false.
        220 * @template EVENTOBJ
        221 */
        222goog.events.Listenable.prototype.fireListeners;
        223
        224
        225/**
        226 * Gets all listeners in this listenable for the given type and
        227 * capture mode.
        228 *
        229 * @param {string|!goog.events.EventId} type The type of the listeners to fire.
        230 * @param {boolean} capture The capture mode of the listeners to fire.
        231 * @return {!Array<goog.events.ListenableKey>} An array of registered
        232 * listeners.
        233 * @template EVENTOBJ
        234 */
        235goog.events.Listenable.prototype.getListeners;
        236
        237
        238/**
        239 * Gets the goog.events.ListenableKey for the event or null if no such
        240 * listener is in use.
        241 *
        242 * @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event
        243 * without the 'on' prefix.
        244 * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The
        245 * listener function to get.
        246 * @param {boolean} capture Whether the listener is a capturing listener.
        247 * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
        248 * listener.
        249 * @return {goog.events.ListenableKey} the found listener or null if not found.
        250 * @template SCOPE,EVENTOBJ
        251 */
        252goog.events.Listenable.prototype.getListener;
        253
        254
        255/**
        256 * Whether there is any active listeners matching the specified
        257 * signature. If either the type or capture parameters are
        258 * unspecified, the function will match on the remaining criteria.
        259 *
        260 * @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.
        261 * @param {boolean=} opt_capture Whether to check for capture or bubble
        262 * listeners.
        263 * @return {boolean} Whether there is any active listeners matching
        264 * the requested type and/or capture phase.
        265 * @template EVENTOBJ
        266 */
        267goog.events.Listenable.prototype.hasListener;
        268
        269
        270
        271/**
        272 * An interface that describes a single registered listener.
        273 * @interface
        274 */
        275goog.events.ListenableKey = function() {};
        276
        277
        278/**
        279 * Counter used to create a unique key
        280 * @type {number}
        281 * @private
        282 */
        283goog.events.ListenableKey.counter_ = 0;
        284
        285
        286/**
        287 * Reserves a key to be used for ListenableKey#key field.
        288 * @return {number} A number to be used to fill ListenableKey#key
        289 * field.
        290 */
        291goog.events.ListenableKey.reserveKey = function() {
        292 return ++goog.events.ListenableKey.counter_;
        293};
        294
        295
        296/**
        297 * The source event target.
        298 * @type {Object|goog.events.Listenable|goog.events.EventTarget}
        299 */
        300goog.events.ListenableKey.prototype.src;
        301
        302
        303/**
        304 * The event type the listener is listening to.
        305 * @type {string}
        306 */
        307goog.events.ListenableKey.prototype.type;
        308
        309
        310/**
        311 * The listener function.
        312 * @type {function(?):?|{handleEvent:function(?):?}|null}
        313 */
        314goog.events.ListenableKey.prototype.listener;
        315
        316
        317/**
        318 * Whether the listener works on capture phase.
        319 * @type {boolean}
        320 */
        321goog.events.ListenableKey.prototype.capture;
        322
        323
        324/**
        325 * The 'this' object for the listener function's scope.
        326 * @type {Object|undefined}
        327 */
        328goog.events.ListenableKey.prototype.handler;
        329
        330
        331/**
        332 * A globally unique number to identify the key.
        333 * @type {number}
        334 */
        335goog.events.ListenableKey.prototype.key;
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/listener.js.src.html b/docs/source/lib/goog/events/listener.js.src.html new file mode 100644 index 0000000..22d0c1e --- /dev/null +++ b/docs/source/lib/goog/events/listener.js.src.html @@ -0,0 +1 @@ +listener.js

        lib/goog/events/listener.js

        1// Copyright 2005 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 Listener object.
        17 * @see ../demos/events.html
        18 */
        19
        20goog.provide('goog.events.Listener');
        21
        22goog.require('goog.events.ListenableKey');
        23
        24
        25
        26/**
        27 * Simple class that stores information about a listener
        28 * @param {!Function} listener Callback function.
        29 * @param {Function} proxy Wrapper for the listener that patches the event.
        30 * @param {EventTarget|goog.events.Listenable} src Source object for
        31 * the event.
        32 * @param {string} type Event type.
        33 * @param {boolean} capture Whether in capture or bubble phase.
        34 * @param {Object=} opt_handler Object in whose context to execute the callback.
        35 * @implements {goog.events.ListenableKey}
        36 * @constructor
        37 */
        38goog.events.Listener = function(
        39 listener, proxy, src, type, capture, opt_handler) {
        40 if (goog.events.Listener.ENABLE_MONITORING) {
        41 this.creationStack = new Error().stack;
        42 }
        43
        44 /**
        45 * Callback function.
        46 * @type {Function}
        47 */
        48 this.listener = listener;
        49
        50 /**
        51 * A wrapper over the original listener. This is used solely to
        52 * handle native browser events (it is used to simulate the capture
        53 * phase and to patch the event object).
        54 * @type {Function}
        55 */
        56 this.proxy = proxy;
        57
        58 /**
        59 * Object or node that callback is listening to
        60 * @type {EventTarget|goog.events.Listenable}
        61 */
        62 this.src = src;
        63
        64 /**
        65 * The event type.
        66 * @const {string}
        67 */
        68 this.type = type;
        69
        70 /**
        71 * Whether the listener is being called in the capture or bubble phase
        72 * @const {boolean}
        73 */
        74 this.capture = !!capture;
        75
        76 /**
        77 * Optional object whose context to execute the listener in
        78 * @type {Object|undefined}
        79 */
        80 this.handler = opt_handler;
        81
        82 /**
        83 * The key of the listener.
        84 * @const {number}
        85 * @override
        86 */
        87 this.key = goog.events.ListenableKey.reserveKey();
        88
        89 /**
        90 * Whether to remove the listener after it has been called.
        91 * @type {boolean}
        92 */
        93 this.callOnce = false;
        94
        95 /**
        96 * Whether the listener has been removed.
        97 * @type {boolean}
        98 */
        99 this.removed = false;
        100};
        101
        102
        103/**
        104 * @define {boolean} Whether to enable the monitoring of the
        105 * goog.events.Listener instances. Switching on the monitoring is only
        106 * recommended for debugging because it has a significant impact on
        107 * performance and memory usage. If switched off, the monitoring code
        108 * compiles down to 0 bytes.
        109 */
        110goog.define('goog.events.Listener.ENABLE_MONITORING', false);
        111
        112
        113/**
        114 * If monitoring the goog.events.Listener instances is enabled, stores the
        115 * creation stack trace of the Disposable instance.
        116 * @type {string}
        117 */
        118goog.events.Listener.prototype.creationStack;
        119
        120
        121/**
        122 * Marks this listener as removed. This also remove references held by
        123 * this listener object (such as listener and event source).
        124 */
        125goog.events.Listener.prototype.markAsRemoved = function() {
        126 this.removed = true;
        127 this.listener = null;
        128 this.proxy = null;
        129 this.src = null;
        130 this.handler = null;
        131};
        \ No newline at end of file diff --git a/docs/source/lib/goog/events/listenermap.js.src.html b/docs/source/lib/goog/events/listenermap.js.src.html new file mode 100644 index 0000000..67b711e --- /dev/null +++ b/docs/source/lib/goog/events/listenermap.js.src.html @@ -0,0 +1 @@ +listenermap.js

        lib/goog/events/listenermap.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 A map of listeners that provides utility functions to
        17 * deal with listeners on an event target. Used by
        18 * {@code goog.events.EventTarget}.
        19 *
        20 * WARNING: Do not use this class from outside goog.events package.
        21 *
        22 * @visibility {//closure/goog/bin/sizetests:__pkg__}
        23 * @visibility {//closure/goog/events:__pkg__}
        24 * @visibility {//closure/goog/labs/events:__pkg__}
        25 */
        26
        27goog.provide('goog.events.ListenerMap');
        28
        29goog.require('goog.array');
        30goog.require('goog.events.Listener');
        31goog.require('goog.object');
        32
        33
        34
        35/**
        36 * Creates a new listener map.
        37 * @param {EventTarget|goog.events.Listenable} src The src object.
        38 * @constructor
        39 * @final
        40 */
        41goog.events.ListenerMap = function(src) {
        42 /** @type {EventTarget|goog.events.Listenable} */
        43 this.src = src;
        44
        45 /**
        46 * Maps of event type to an array of listeners.
        47 * @type {Object<string, !Array<!goog.events.Listener>>}
        48 */
        49 this.listeners = {};
        50
        51 /**
        52 * The count of types in this map that have registered listeners.
        53 * @private {number}
        54 */
        55 this.typeCount_ = 0;
        56};
        57
        58
        59/**
        60 * @return {number} The count of event types in this map that actually
        61 * have registered listeners.
        62 */
        63goog.events.ListenerMap.prototype.getTypeCount = function() {
        64 return this.typeCount_;
        65};
        66
        67
        68/**
        69 * @return {number} Total number of registered listeners.
        70 */
        71goog.events.ListenerMap.prototype.getListenerCount = function() {
        72 var count = 0;
        73 for (var type in this.listeners) {
        74 count += this.listeners[type].length;
        75 }
        76 return count;
        77};
        78
        79
        80/**
        81 * Adds an event listener. A listener can only be added once to an
        82 * object and if it is added again the key for the listener is
        83 * returned.
        84 *
        85 * Note that a one-off listener will not change an existing listener,
        86 * if any. On the other hand a normal listener will change existing
        87 * one-off listener to become a normal listener.
        88 *
        89 * @param {string|!goog.events.EventId} type The listener event type.
        90 * @param {!Function} listener This listener callback method.
        91 * @param {boolean} callOnce Whether the listener is a one-off
        92 * listener.
        93 * @param {boolean=} opt_useCapture The capture mode of the listener.
        94 * @param {Object=} opt_listenerScope Object in whose scope to call the
        95 * listener.
        96 * @return {goog.events.ListenableKey} Unique key for the listener.
        97 */
        98goog.events.ListenerMap.prototype.add = function(
        99 type, listener, callOnce, opt_useCapture, opt_listenerScope) {
        100 var typeStr = type.toString();
        101 var listenerArray = this.listeners[typeStr];
        102 if (!listenerArray) {
        103 listenerArray = this.listeners[typeStr] = [];
        104 this.typeCount_++;
        105 }
        106
        107 var listenerObj;
        108 var index = goog.events.ListenerMap.findListenerIndex_(
        109 listenerArray, listener, opt_useCapture, opt_listenerScope);
        110 if (index > -1) {
        111 listenerObj = listenerArray[index];
        112 if (!callOnce) {
        113 // Ensure that, if there is an existing callOnce listener, it is no
        114 // longer a callOnce listener.
        115 listenerObj.callOnce = false;
        116 }
        117 } else {
        118 listenerObj = new goog.events.Listener(
        119 listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);
        120 listenerObj.callOnce = callOnce;
        121 listenerArray.push(listenerObj);
        122 }
        123 return listenerObj;
        124};
        125
        126
        127/**
        128 * Removes a matching listener.
        129 * @param {string|!goog.events.EventId} type The listener event type.
        130 * @param {!Function} listener This listener callback method.
        131 * @param {boolean=} opt_useCapture The capture mode of the listener.
        132 * @param {Object=} opt_listenerScope Object in whose scope to call the
        133 * listener.
        134 * @return {boolean} Whether any listener was removed.
        135 */
        136goog.events.ListenerMap.prototype.remove = function(
        137 type, listener, opt_useCapture, opt_listenerScope) {
        138 var typeStr = type.toString();
        139 if (!(typeStr in this.listeners)) {
        140 return false;
        141 }
        142
        143 var listenerArray = this.listeners[typeStr];
        144 var index = goog.events.ListenerMap.findListenerIndex_(
        145 listenerArray, listener, opt_useCapture, opt_listenerScope);
        146 if (index > -1) {
        147 var listenerObj = listenerArray[index];
        148 listenerObj.markAsRemoved();
        149 goog.array.removeAt(listenerArray, index);
        150 if (listenerArray.length == 0) {
        151 delete this.listeners[typeStr];
        152 this.typeCount_--;
        153 }
        154 return true;
        155 }
        156 return false;
        157};
        158
        159
        160/**
        161 * Removes the given listener object.
        162 * @param {goog.events.ListenableKey} listener The listener to remove.
        163 * @return {boolean} Whether the listener is removed.
        164 */
        165goog.events.ListenerMap.prototype.removeByKey = function(listener) {
        166 var type = listener.type;
        167 if (!(type in this.listeners)) {
        168 return false;
        169 }
        170
        171 var removed = goog.array.remove(this.listeners[type], listener);
        172 if (removed) {
        173 listener.markAsRemoved();
        174 if (this.listeners[type].length == 0) {
        175 delete this.listeners[type];
        176 this.typeCount_--;
        177 }
        178 }
        179 return removed;
        180};
        181
        182
        183/**
        184 * Removes all listeners from this map. If opt_type is provided, only
        185 * listeners that match the given type are removed.
        186 * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
        187 * @return {number} Number of listeners removed.
        188 */
        189goog.events.ListenerMap.prototype.removeAll = function(opt_type) {
        190 var typeStr = opt_type && opt_type.toString();
        191 var count = 0;
        192 for (var type in this.listeners) {
        193 if (!typeStr || type == typeStr) {
        194 var listenerArray = this.listeners[type];
        195 for (var i = 0; i < listenerArray.length; i++) {
        196 ++count;
        197 listenerArray[i].markAsRemoved();
        198 }
        199 delete this.listeners[type];
        200 this.typeCount_--;
        201 }
        202 }
        203 return count;
        204};
        205
        206
        207/**
        208 * Gets all listeners that match the given type and capture mode. The
        209 * returned array is a copy (but the listener objects are not).
        210 * @param {string|!goog.events.EventId} type The type of the listeners
        211 * to retrieve.
        212 * @param {boolean} capture The capture mode of the listeners to retrieve.
        213 * @return {!Array<goog.events.ListenableKey>} An array of matching
        214 * listeners.
        215 */
        216goog.events.ListenerMap.prototype.getListeners = function(type, capture) {
        217 var listenerArray = this.listeners[type.toString()];
        218 var rv = [];
        219 if (listenerArray) {
        220 for (var i = 0; i < listenerArray.length; ++i) {
        221 var listenerObj = listenerArray[i];
        222 if (listenerObj.capture == capture) {
        223 rv.push(listenerObj);
        224 }
        225 }
        226 }
        227 return rv;
        228};
        229
        230
        231/**
        232 * Gets the goog.events.ListenableKey for the event or null if no such
        233 * listener is in use.
        234 *
        235 * @param {string|!goog.events.EventId} type The type of the listener
        236 * to retrieve.
        237 * @param {!Function} listener The listener function to get.
        238 * @param {boolean} capture Whether the listener is a capturing listener.
        239 * @param {Object=} opt_listenerScope Object in whose scope to call the
        240 * listener.
        241 * @return {goog.events.ListenableKey} the found listener or null if not found.
        242 */
        243goog.events.ListenerMap.prototype.getListener = function(
        244 type, listener, capture, opt_listenerScope) {
        245 var listenerArray = this.listeners[type.toString()];
        246 var i = -1;
        247 if (listenerArray) {
        248 i = goog.events.ListenerMap.findListenerIndex_(
        249 listenerArray, listener, capture, opt_listenerScope);
        250 }
        251 return i > -1 ? listenerArray[i] : null;
        252};
        253
        254
        255/**
        256 * Whether there is a matching listener. If either the type or capture
        257 * parameters are unspecified, the function will match on the
        258 * remaining criteria.
        259 *
        260 * @param {string|!goog.events.EventId=} opt_type The type of the listener.
        261 * @param {boolean=} opt_capture The capture mode of the listener.
        262 * @return {boolean} Whether there is an active listener matching
        263 * the requested type and/or capture phase.
        264 */
        265goog.events.ListenerMap.prototype.hasListener = function(
        266 opt_type, opt_capture) {
        267 var hasType = goog.isDef(opt_type);
        268 var typeStr = hasType ? opt_type.toString() : '';
        269 var hasCapture = goog.isDef(opt_capture);
        270
        271 return goog.object.some(
        272 this.listeners, function(listenerArray, type) {
        273 for (var i = 0; i < listenerArray.length; ++i) {
        274 if ((!hasType || listenerArray[i].type == typeStr) &&
        275 (!hasCapture || listenerArray[i].capture == opt_capture)) {
        276 return true;
        277 }
        278 }
        279
        280 return false;
        281 });
        282};
        283
        284
        285/**
        286 * Finds the index of a matching goog.events.Listener in the given
        287 * listenerArray.
        288 * @param {!Array<!goog.events.Listener>} listenerArray Array of listener.
        289 * @param {!Function} listener The listener function.
        290 * @param {boolean=} opt_useCapture The capture flag for the listener.
        291 * @param {Object=} opt_listenerScope The listener scope.
        292 * @return {number} The index of the matching listener within the
        293 * listenerArray.
        294 * @private
        295 */
        296goog.events.ListenerMap.findListenerIndex_ = function(
        297 listenerArray, listener, opt_useCapture, opt_listenerScope) {
        298 for (var i = 0; i < listenerArray.length; ++i) {
        299 var listenerObj = listenerArray[i];
        300 if (!listenerObj.removed &&
        301 listenerObj.listener == listener &&
        302 listenerObj.capture == !!opt_useCapture &&
        303 listenerObj.handler == opt_listenerScope) {
        304 return i;
        305 }
        306 }
        307 return -1;
        308};
        \ No newline at end of file diff --git a/docs/source/lib/goog/fs/url.js.src.html b/docs/source/lib/goog/fs/url.js.src.html new file mode 100644 index 0000000..7d7bb87 --- /dev/null +++ b/docs/source/lib/goog/fs/url.js.src.html @@ -0,0 +1 @@ +url.js

        lib/goog/fs/url.js

        1// Copyright 2015 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 Wrapper for URL and its createObjectUrl and revokeObjectUrl
        17 * methods that are part of the HTML5 File API.
        18 */
        19
        20goog.provide('goog.fs.url');
        21
        22
        23/**
        24 * Creates a blob URL for a blob object.
        25 * Throws an error if the browser does not support Object Urls.
        26 *
        27 * @param {!Blob} blob The object for which to create the URL.
        28 * @return {string} The URL for the object.
        29 */
        30goog.fs.url.createObjectUrl = function(blob) {
        31 return goog.fs.url.getUrlObject_().createObjectURL(blob);
        32};
        33
        34
        35/**
        36 * Revokes a URL created by {@link goog.fs.url.createObjectUrl}.
        37 * Throws an error if the browser does not support Object Urls.
        38 *
        39 * @param {string} url The URL to revoke.
        40 */
        41goog.fs.url.revokeObjectUrl = function(url) {
        42 goog.fs.url.getUrlObject_().revokeObjectURL(url);
        43};
        44
        45
        46/**
        47 * @typedef {{createObjectURL: (function(!Blob): string),
        48 * revokeObjectURL: function(string): void}}
        49 */
        50goog.fs.url.UrlObject_;
        51
        52
        53/**
        54 * Get the object that has the createObjectURL and revokeObjectURL functions for
        55 * this browser.
        56 *
        57 * @return {goog.fs.url.UrlObject_} The object for this browser.
        58 * @private
        59 */
        60goog.fs.url.getUrlObject_ = function() {
        61 var urlObject = goog.fs.url.findUrlObject_();
        62 if (urlObject != null) {
        63 return urlObject;
        64 } else {
        65 throw Error('This browser doesn\'t seem to support blob URLs');
        66 }
        67};
        68
        69
        70/**
        71 * Finds the object that has the createObjectURL and revokeObjectURL functions
        72 * for this browser.
        73 *
        74 * @return {?goog.fs.url.UrlObject_} The object for this browser or null if the
        75 * browser does not support Object Urls.
        76 * @suppress {unnecessaryCasts} Depending on how the code is compiled, casting
        77 * goog.global to UrlObject_ may result in unnecessary cast warning.
        78 * However, the cast cannot be removed because with different set of
        79 * compiler flags, the cast is indeed necessary. As such, silencing it.
        80 * @private
        81 */
        82goog.fs.url.findUrlObject_ = function() {
        83 // This is what the spec says to do
        84 // http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL
        85 if (goog.isDef(goog.global.URL) &&
        86 goog.isDef(goog.global.URL.createObjectURL)) {
        87 return /** @type {goog.fs.url.UrlObject_} */ (goog.global.URL);
        88 // This is what Chrome does (as of 10.0.648.6 dev)
        89 } else if (goog.isDef(goog.global.webkitURL) &&
        90 goog.isDef(goog.global.webkitURL.createObjectURL)) {
        91 return /** @type {goog.fs.url.UrlObject_} */ (goog.global.webkitURL);
        92 // This is what the spec used to say to do
        93 } else if (goog.isDef(goog.global.createObjectURL)) {
        94 return /** @type {goog.fs.url.UrlObject_} */ (goog.global);
        95 } else {
        96 return null;
        97 }
        98};
        99
        100
        101/**
        102 * Checks whether this browser supports Object Urls. If not, calls to
        103 * createObjectUrl and revokeObjectUrl will result in an error.
        104 *
        105 * @return {boolean} True if this browser supports Object Urls.
        106 */
        107goog.fs.url.browserSupportsObjectUrls = function() {
        108 return goog.fs.url.findUrlObject_() != null;
        109};
        \ No newline at end of file diff --git a/docs/source/lib/goog/functions/functions.js.src.html b/docs/source/lib/goog/functions/functions.js.src.html new file mode 100644 index 0000000..b64eaf8 --- /dev/null +++ b/docs/source/lib/goog/functions/functions.js.src.html @@ -0,0 +1 @@ +functions.js

        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
        23goog.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 */
        32goog.functions.constant = function(retValue) {
        33 return function() {
        34 return retValue;
        35 };
        36};
        37
        38
        39/**
        40 * Always returns false.
        41 * @type {function(...): boolean}
        42 */
        43goog.functions.FALSE = goog.functions.constant(false);
        44
        45
        46/**
        47 * Always returns true.
        48 * @type {function(...): boolean}
        49 */
        50goog.functions.TRUE = goog.functions.constant(true);
        51
        52
        53/**
        54 * Always returns NULL.
        55 * @type {function(...): null}
        56 */
        57goog.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 */
        68goog.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 */
        78goog.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 */
        90goog.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 */
        105goog.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 */
        118goog.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 */
        133goog.functions.withReturnValue = function(f, retValue) {
        134 return goog.functions.sequence(f, goog.functions.constant(retValue));
        135};
        136
        137
        138/**
        139 * Creates a function that returns whether its arguement equals the given value.
        140 *
        141 * Example:
        142 * var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));
        143 *
        144 * @param {*} value The value to compare to.
        145 * @param {boolean=} opt_useLooseComparison Whether to use a loose (==)
        146 * comparison rather than a strict (===) one. Defaults to false.
        147 * @return {function(*):boolean} The new function.
        148 */
        149goog.functions.equalTo = function(value, opt_useLooseComparison) {
        150 return function(other) {
        151 return opt_useLooseComparison ? (value == other) : (value === other);
        152 };
        153};
        154
        155
        156/**
        157 * Creates the composition of the functions passed in.
        158 * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
        159 * @param {function(...?):T} fn The final function.
        160 * @param {...Function} var_args A list of functions.
        161 * @return {function(...?):T} The composition of all inputs.
        162 * @template T
        163 */
        164goog.functions.compose = function(fn, var_args) {
        165 var functions = arguments;
        166 var length = functions.length;
        167 return function() {
        168 var result;
        169 if (length) {
        170 result = functions[length - 1].apply(this, arguments);
        171 }
        172
        173 for (var i = length - 2; i >= 0; i--) {
        174 result = functions[i].call(this, result);
        175 }
        176 return result;
        177 };
        178};
        179
        180
        181/**
        182 * Creates a function that calls the functions passed in in sequence, and
        183 * returns the value of the last function. For example,
        184 * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).
        185 * @param {...Function} var_args A list of functions.
        186 * @return {!Function} A function that calls all inputs in sequence.
        187 */
        188goog.functions.sequence = function(var_args) {
        189 var functions = arguments;
        190 var length = functions.length;
        191 return function() {
        192 var result;
        193 for (var i = 0; i < length; i++) {
        194 result = functions[i].apply(this, arguments);
        195 }
        196 return result;
        197 };
        198};
        199
        200
        201/**
        202 * Creates a function that returns true if each of its components evaluates
        203 * to true. The components are evaluated in order, and the evaluation will be
        204 * short-circuited as soon as a function returns false.
        205 * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).
        206 * @param {...Function} var_args A list of functions.
        207 * @return {function(...?):boolean} A function that ANDs its component
        208 * functions.
        209 */
        210goog.functions.and = function(var_args) {
        211 var functions = arguments;
        212 var length = functions.length;
        213 return function() {
        214 for (var i = 0; i < length; i++) {
        215 if (!functions[i].apply(this, arguments)) {
        216 return false;
        217 }
        218 }
        219 return true;
        220 };
        221};
        222
        223
        224/**
        225 * Creates a function that returns true if any of its components evaluates
        226 * to true. The components are evaluated in order, and the evaluation will be
        227 * short-circuited as soon as a function returns true.
        228 * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).
        229 * @param {...Function} var_args A list of functions.
        230 * @return {function(...?):boolean} A function that ORs its component
        231 * functions.
        232 */
        233goog.functions.or = function(var_args) {
        234 var functions = arguments;
        235 var length = functions.length;
        236 return function() {
        237 for (var i = 0; i < length; i++) {
        238 if (functions[i].apply(this, arguments)) {
        239 return true;
        240 }
        241 }
        242 return false;
        243 };
        244};
        245
        246
        247/**
        248 * Creates a function that returns the Boolean opposite of a provided function.
        249 * For example, (goog.functions.not(f))(x) is equivalent to !f(x).
        250 * @param {!Function} f The original function.
        251 * @return {function(...?):boolean} A function that delegates to f and returns
        252 * opposite.
        253 */
        254goog.functions.not = function(f) {
        255 return function() {
        256 return !f.apply(this, arguments);
        257 };
        258};
        259
        260
        261/**
        262 * Generic factory function to construct an object given the constructor
        263 * and the arguments. Intended to be bound to create object factories.
        264 *
        265 * Example:
        266 *
        267 * var factory = goog.partial(goog.functions.create, Class);
        268 *
        269 * @param {function(new:T, ...)} constructor The constructor for the Object.
        270 * @param {...*} var_args The arguments to be passed to the constructor.
        271 * @return {T} A new instance of the class given in {@code constructor}.
        272 * @template T
        273 */
        274goog.functions.create = function(constructor, var_args) {
        275 /**
        276 * @constructor
        277 * @final
        278 */
        279 var temp = function() {};
        280 temp.prototype = constructor.prototype;
        281
        282 // obj will have constructor's prototype in its chain and
        283 // 'obj instanceof constructor' will be true.
        284 var obj = new temp();
        285
        286 // obj is initialized by constructor.
        287 // arguments is only array-like so lacks shift(), but can be used with
        288 // the Array prototype function.
        289 constructor.apply(obj, Array.prototype.slice.call(arguments, 1));
        290 return obj;
        291};
        292
        293
        294/**
        295 * @define {boolean} Whether the return value cache should be used.
        296 * This should only be used to disable caches when testing.
        297 */
        298goog.define('goog.functions.CACHE_RETURN_VALUE', true);
        299
        300
        301/**
        302 * Gives a wrapper function that caches the return value of a parameterless
        303 * function when first called.
        304 *
        305 * When called for the first time, the given function is called and its
        306 * return value is cached (thus this is only appropriate for idempotent
        307 * functions). Subsequent calls will return the cached return value. This
        308 * allows the evaluation of expensive functions to be delayed until first used.
        309 *
        310 * To cache the return values of functions with parameters, see goog.memoize.
        311 *
        312 * @param {!function():T} fn A function to lazily evaluate.
        313 * @return {!function():T} A wrapped version the function.
        314 * @template T
        315 */
        316goog.functions.cacheReturnValue = function(fn) {
        317 var called = false;
        318 var value;
        319
        320 return function() {
        321 if (!goog.functions.CACHE_RETURN_VALUE) {
        322 return fn();
        323 }
        324
        325 if (!called) {
        326 value = fn();
        327 called = true;
        328 }
        329
        330 return value;
        331 }
        332};
        333
        334
        335/**
        336 * Wraps a function to allow it to be called, at most, once. All
        337 * additional calls are no-ops.
        338 *
        339 * This is particularly useful for initialization functions
        340 * that should be called, at most, once.
        341 *
        342 * @param {function():*} f Function to call.
        343 * @return {function():undefined} Wrapped function.
        344 */
        345goog.functions.once = function(f) {
        346 // Keep a reference to the function that we null out when we're done with
        347 // it -- that way, the function can be GC'd when we're done with it.
        348 var inner = f;
        349 return function() {
        350 if (inner) {
        351 var tmp = inner;
        352 inner = null;
        353 tmp();
        354 }
        355 };
        356};
        357
        358
        359/**
        360 * Wraps a function to allow it to be called, at most, once for each sequence of
        361 * calls fired repeatedly so long as they are fired less than a specified
        362 * interval apart (in milliseconds). Whether it receives one signal or multiple,
        363 * it will always wait until a full interval has elapsed since the last signal
        364 * before performing the action, passing the arguments from the last call of the
        365 * debouncing decorator into the decorated function.
        366 *
        367 * This is particularly useful for bulking up repeated user actions (e.g. only
        368 * refreshing a view once a user finishes typing rather than updating with every
        369 * keystroke). For more stateful debouncing with support for pausing, resuming,
        370 * and canceling debounced actions, use {@code goog.async.Debouncer}.
        371 *
        372 * @param {function(this:SCOPE, ...?)} f Function to call.
        373 * @param {number} interval Interval over which to debounce. The function will
        374 * only be called after the full interval has elapsed since the last call.
        375 * @param {SCOPE=} opt_scope Object in whose scope to call the function.
        376 * @return {function(...?): undefined} Wrapped function.
        377 * @template SCOPE
        378 */
        379goog.functions.debounce = function(f, interval, opt_scope) {
        380 if (opt_scope) {
        381 f = goog.bind(f, opt_scope);
        382 }
        383 var timeout = null;
        384 return /** @type {function(...?)} */ (function(var_args) {
        385 goog.global.clearTimeout(timeout);
        386 var args = arguments;
        387 timeout = goog.global.setTimeout(function() {
        388 f.apply(null, args);
        389 }, interval);
        390 });
        391};
        392
        393
        394/**
        395 * Wraps a function to allow it to be called, at most, once per interval
        396 * (specified in milliseconds). If it is called multiple times while it is
        397 * waiting, it will only perform the action once at the end of the interval,
        398 * passing the arguments from the last call of the throttling decorator into the
        399 * decorated function.
        400 *
        401 * This is particularly useful for limiting repeated user requests (e.g.
        402 * preventing a user from spamming a server with frequent view refreshes). For
        403 * more stateful throttling with support for pausing, resuming, and canceling
        404 * throttled actions, use {@code goog.async.Throttle}.
        405 *
        406 * @param {function(this:SCOPE, ...?)} f Function to call.
        407 * @param {number} interval Interval over which to throttle. The function can
        408 * only be called once per interval.
        409 * @param {SCOPE=} opt_scope Object in whose scope to call the function.
        410 * @return {function(...?): undefined} Wrapped function.
        411 * @template SCOPE
        412 */
        413goog.functions.throttle = function(f, interval, opt_scope) {
        414 if (opt_scope) {
        415 f = goog.bind(f, opt_scope);
        416 }
        417 var timeout = null;
        418 var shouldFire = false;
        419 var args = [];
        420
        421 var handleTimeout = function() {
        422 timeout = null;
        423 if (shouldFire) {
        424 shouldFire = false;
        425 fire();
        426 }
        427 };
        428
        429 var fire = function() {
        430 timeout = goog.global.setTimeout(handleTimeout, interval);
        431 f.apply(null, args);
        432 };
        433
        434 return /** @type {function(...?)} */ (function(var_args) {
        435 args = arguments;
        436 if (!timeout) {
        437 fire();
        438 } else {
        439 shouldFire = true;
        440 }
        441 });
        442};
        \ No newline at end of file diff --git a/docs/source/lib/goog/html/safehtml.js.src.html b/docs/source/lib/goog/html/safehtml.js.src.html new file mode 100644 index 0000000..a7da629 --- /dev/null +++ b/docs/source/lib/goog/html/safehtml.js.src.html @@ -0,0 +1 @@ +safehtml.js

        lib/goog/html/safehtml.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/**
        17 * @fileoverview The SafeHtml type and its builders.
        18 *
        19 * TODO(xtof): Link to document stating type contract.
        20 */
        21
        22goog.provide('goog.html.SafeHtml');
        23
        24goog.require('goog.array');
        25goog.require('goog.asserts');
        26goog.require('goog.dom.TagName');
        27goog.require('goog.dom.tags');
        28goog.require('goog.html.SafeStyle');
        29goog.require('goog.html.SafeStyleSheet');
        30goog.require('goog.html.SafeUrl');
        31goog.require('goog.html.TrustedResourceUrl');
        32goog.require('goog.i18n.bidi.Dir');
        33goog.require('goog.i18n.bidi.DirectionalString');
        34goog.require('goog.object');
        35goog.require('goog.string');
        36goog.require('goog.string.Const');
        37goog.require('goog.string.TypedString');
        38
        39
        40
        41/**
        42 * A string that is safe to use in HTML context in DOM APIs and HTML documents.
        43 *
        44 * A SafeHtml is a string-like object that carries the security type contract
        45 * that its value as a string will not cause untrusted script execution when
        46 * evaluated as HTML in a browser.
        47 *
        48 * Values of this type are guaranteed to be safe to use in HTML contexts,
        49 * such as, assignment to the innerHTML DOM property, or interpolation into
        50 * a HTML template in HTML PC_DATA context, in the sense that the use will not
        51 * result in a Cross-Site-Scripting vulnerability.
        52 *
        53 * Instances of this type must be created via the factory methods
        54 * ({@code goog.html.SafeHtml.create}, {@code goog.html.SafeHtml.htmlEscape}),
        55 * etc and not by invoking its constructor. The constructor intentionally
        56 * takes no parameters and the type is immutable; hence only a default instance
        57 * corresponding to the empty string can be obtained via constructor invocation.
        58 *
        59 * @see goog.html.SafeHtml#create
        60 * @see goog.html.SafeHtml#htmlEscape
        61 * @constructor
        62 * @final
        63 * @struct
        64 * @implements {goog.i18n.bidi.DirectionalString}
        65 * @implements {goog.string.TypedString}
        66 */
        67goog.html.SafeHtml = function() {
        68 /**
        69 * The contained value of this SafeHtml. The field has a purposely ugly
        70 * name to make (non-compiled) code that attempts to directly access this
        71 * field stand out.
        72 * @private {string}
        73 */
        74 this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
        75
        76 /**
        77 * A type marker used to implement additional run-time type checking.
        78 * @see goog.html.SafeHtml#unwrap
        79 * @const
        80 * @private
        81 */
        82 this.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
        83 goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
        84
        85 /**
        86 * This SafeHtml's directionality, or null if unknown.
        87 * @private {?goog.i18n.bidi.Dir}
        88 */
        89 this.dir_ = null;
        90};
        91
        92
        93/**
        94 * @override
        95 * @const
        96 */
        97goog.html.SafeHtml.prototype.implementsGoogI18nBidiDirectionalString = true;
        98
        99
        100/** @override */
        101goog.html.SafeHtml.prototype.getDirection = function() {
        102 return this.dir_;
        103};
        104
        105
        106/**
        107 * @override
        108 * @const
        109 */
        110goog.html.SafeHtml.prototype.implementsGoogStringTypedString = true;
        111
        112
        113/**
        114 * Returns this SafeHtml's value a string.
        115 *
        116 * IMPORTANT: In code where it is security relevant that an object's type is
        117 * indeed {@code SafeHtml}, use {@code goog.html.SafeHtml.unwrap} instead of
        118 * this method. If in doubt, assume that it's security relevant. In particular,
        119 * note that goog.html functions which return a goog.html type do not guarantee
        120 * that the returned instance is of the right type. For example:
        121 *
        122 * <pre>
        123 * var fakeSafeHtml = new String('fake');
        124 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
        125 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
        126 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
        127 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
        128 * // instanceof goog.html.SafeHtml.
        129 * </pre>
        130 *
        131 * @see goog.html.SafeHtml#unwrap
        132 * @override
        133 */
        134goog.html.SafeHtml.prototype.getTypedStringValue = function() {
        135 return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
        136};
        137
        138
        139if (goog.DEBUG) {
        140 /**
        141 * Returns a debug string-representation of this value.
        142 *
        143 * To obtain the actual string value wrapped in a SafeHtml, use
        144 * {@code goog.html.SafeHtml.unwrap}.
        145 *
        146 * @see goog.html.SafeHtml#unwrap
        147 * @override
        148 */
        149 goog.html.SafeHtml.prototype.toString = function() {
        150 return 'SafeHtml{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
        151 '}';
        152 };
        153}
        154
        155
        156/**
        157 * Performs a runtime check that the provided object is indeed a SafeHtml
        158 * object, and returns its value.
        159 * @param {!goog.html.SafeHtml} safeHtml The object to extract from.
        160 * @return {string} The SafeHtml object's contained string, unless the run-time
        161 * type check fails. In that case, {@code unwrap} returns an innocuous
        162 * string, or, if assertions are enabled, throws
        163 * {@code goog.asserts.AssertionError}.
        164 */
        165goog.html.SafeHtml.unwrap = function(safeHtml) {
        166 // Perform additional run-time type-checking to ensure that safeHtml is indeed
        167 // an instance of the expected type. This provides some additional protection
        168 // against security bugs due to application code that disables type checks.
        169 // Specifically, the following checks are performed:
        170 // 1. The object is an instance of the expected type.
        171 // 2. The object is not an instance of a subclass.
        172 // 3. The object carries a type marker for the expected type. "Faking" an
        173 // object requires a reference to the type marker, which has names intended
        174 // to stand out in code reviews.
        175 if (safeHtml instanceof goog.html.SafeHtml &&
        176 safeHtml.constructor === goog.html.SafeHtml &&
        177 safeHtml.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
        178 goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
        179 return safeHtml.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
        180 } else {
        181 goog.asserts.fail('expected object of type SafeHtml, got \'' +
        182 safeHtml + '\'');
        183 return 'type_error:SafeHtml';
        184 }
        185};
        186
        187
        188/**
        189 * Shorthand for union of types that can sensibly be converted to strings
        190 * or might already be SafeHtml (as SafeHtml is a goog.string.TypedString).
        191 * @private
        192 * @typedef {string|number|boolean|!goog.string.TypedString|
        193 * !goog.i18n.bidi.DirectionalString}
        194 */
        195goog.html.SafeHtml.TextOrHtml_;
        196
        197
        198/**
        199 * Returns HTML-escaped text as a SafeHtml object.
        200 *
        201 * If text is of a type that implements
        202 * {@code goog.i18n.bidi.DirectionalString}, the directionality of the new
        203 * {@code SafeHtml} object is set to {@code text}'s directionality, if known.
        204 * Otherwise, the directionality of the resulting SafeHtml is unknown (i.e.,
        205 * {@code null}).
        206 *
        207 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
        208 * the parameter is of type SafeHtml it is returned directly (no escaping
        209 * is done).
        210 * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
        211 */
        212goog.html.SafeHtml.htmlEscape = function(textOrHtml) {
        213 if (textOrHtml instanceof goog.html.SafeHtml) {
        214 return textOrHtml;
        215 }
        216 var dir = null;
        217 if (textOrHtml.implementsGoogI18nBidiDirectionalString) {
        218 dir = textOrHtml.getDirection();
        219 }
        220 var textAsString;
        221 if (textOrHtml.implementsGoogStringTypedString) {
        222 textAsString = textOrHtml.getTypedStringValue();
        223 } else {
        224 textAsString = String(textOrHtml);
        225 }
        226 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        227 goog.string.htmlEscape(textAsString), dir);
        228};
        229
        230
        231/**
        232 * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
        233 * &lt;br&gt;.
        234 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
        235 * the parameter is of type SafeHtml it is returned directly (no escaping
        236 * is done).
        237 * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
        238 */
        239goog.html.SafeHtml.htmlEscapePreservingNewlines = function(textOrHtml) {
        240 if (textOrHtml instanceof goog.html.SafeHtml) {
        241 return textOrHtml;
        242 }
        243 var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
        244 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        245 goog.string.newLineToBr(goog.html.SafeHtml.unwrap(html)),
        246 html.getDirection());
        247};
        248
        249
        250/**
        251 * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
        252 * &lt;br&gt; and escaping whitespace to preserve spatial formatting. Character
        253 * entity #160 is used to make it safer for XML.
        254 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
        255 * the parameter is of type SafeHtml it is returned directly (no escaping
        256 * is done).
        257 * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
        258 */
        259goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces = function(
        260 textOrHtml) {
        261 if (textOrHtml instanceof goog.html.SafeHtml) {
        262 return textOrHtml;
        263 }
        264 var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
        265 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        266 goog.string.whitespaceEscape(goog.html.SafeHtml.unwrap(html)),
        267 html.getDirection());
        268};
        269
        270
        271/**
        272 * Coerces an arbitrary object into a SafeHtml object.
        273 *
        274 * If {@code textOrHtml} is already of type {@code goog.html.SafeHtml}, the same
        275 * object is returned. Otherwise, {@code textOrHtml} is coerced to string, and
        276 * HTML-escaped. If {@code textOrHtml} is of a type that implements
        277 * {@code goog.i18n.bidi.DirectionalString}, its directionality, if known, is
        278 * preserved.
        279 *
        280 * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text or SafeHtml to
        281 * coerce.
        282 * @return {!goog.html.SafeHtml} The resulting SafeHtml object.
        283 * @deprecated Use goog.html.SafeHtml.htmlEscape.
        284 */
        285goog.html.SafeHtml.from = goog.html.SafeHtml.htmlEscape;
        286
        287
        288/**
        289 * @const
        290 * @private
        291 */
        292goog.html.SafeHtml.VALID_NAMES_IN_TAG_ = /^[a-zA-Z0-9-]+$/;
        293
        294
        295/**
        296 * Set of attributes containing URL as defined at
        297 * http://www.w3.org/TR/html5/index.html#attributes-1.
        298 * @private @const {!Object<string,boolean>}
        299 */
        300goog.html.SafeHtml.URL_ATTRIBUTES_ = goog.object.createSet('action', 'cite',
        301 'data', 'formaction', 'href', 'manifest', 'poster', 'src');
        302
        303
        304/**
        305 * Tags which are unsupported via create(). They might be supported via a
        306 * tag-specific create method. These are tags which might require a
        307 * TrustedResourceUrl in one of their attributes or a restricted type for
        308 * their content.
        309 * @private @const {!Object<string,boolean>}
        310 */
        311goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_ = goog.object.createSet(
        312 goog.dom.TagName.EMBED, goog.dom.TagName.IFRAME, goog.dom.TagName.LINK,
        313 goog.dom.TagName.OBJECT, goog.dom.TagName.SCRIPT, goog.dom.TagName.STYLE,
        314 goog.dom.TagName.TEMPLATE);
        315
        316
        317/**
        318 * @typedef {string|number|goog.string.TypedString|
        319 * goog.html.SafeStyle.PropertyMap}
        320 * @private
        321 */
        322goog.html.SafeHtml.AttributeValue_;
        323
        324
        325/**
        326 * Creates a SafeHtml content consisting of a tag with optional attributes and
        327 * optional content.
        328 *
        329 * For convenience tag names and attribute names are accepted as regular
        330 * strings, instead of goog.string.Const. Nevertheless, you should not pass
        331 * user-controlled values to these parameters. Note that these parameters are
        332 * syntactically validated at runtime, and invalid values will result in
        333 * an exception.
        334 *
        335 * Example usage:
        336 *
        337 * goog.html.SafeHtml.create('br');
        338 * goog.html.SafeHtml.create('div', {'class': 'a'});
        339 * goog.html.SafeHtml.create('p', {}, 'a');
        340 * goog.html.SafeHtml.create('p', {}, goog.html.SafeHtml.create('br'));
        341 *
        342 * goog.html.SafeHtml.create('span', {
        343 * 'style': {'margin': '0'}
        344 * });
        345 *
        346 * To guarantee SafeHtml's type contract is upheld there are restrictions on
        347 * attribute values and tag names.
        348 *
        349 * - For attributes which contain script code (on*), a goog.string.Const is
        350 * required.
        351 * - For attributes which contain style (style), a goog.html.SafeStyle or a
        352 * goog.html.SafeStyle.PropertyMap is required.
        353 * - For attributes which are interpreted as URLs (e.g. src, href) a
        354 * goog.html.SafeUrl, goog.string.Const or string is required. If a string
        355 * is passed, it will be sanitized with SafeUrl.sanitize().
        356 * - For tags which can load code, more specific goog.html.SafeHtml.create*()
        357 * functions must be used. Tags which can load code and are not supported by
        358 * this function are embed, iframe, link, object, script, style, and template.
        359 *
        360 * @param {string} tagName The name of the tag. Only tag names consisting of
        361 * [a-zA-Z0-9-] are allowed. Tag names documented above are disallowed.
        362 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
        363 * opt_attributes Mapping from attribute names to their values. Only
        364 * attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
        365 * undefined causes the attribute to be omitted.
        366 * @param {!goog.html.SafeHtml.TextOrHtml_|
        367 * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
        368 * HTML-escape and put inside the tag. This must be empty for void tags
        369 * like <br>. Array elements are concatenated.
        370 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
        371 * @throws {Error} If invalid tag name, attribute name, or attribute value is
        372 * provided.
        373 * @throws {goog.asserts.AssertionError} If content for void tag is provided.
        374 */
        375goog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) {
        376 if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(tagName)) {
        377 throw Error('Invalid tag name <' + tagName + '>.');
        378 }
        379 if (tagName.toUpperCase() in goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_) {
        380 throw Error('Tag name <' + tagName + '> is not allowed for SafeHtml.');
        381 }
        382 return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
        383 tagName, opt_attributes, opt_content);
        384};
        385
        386
        387/**
        388 * Creates a SafeHtml representing an iframe tag.
        389 *
        390 * By default the sandbox attribute is set to an empty value, which is the most
        391 * secure option, as it confers the iframe the least privileges. If this
        392 * is too restrictive then granting individual privileges is the preferable
        393 * option. Unsetting the attribute entirely is the least secure option and
        394 * should never be done unless it's stricly necessary.
        395 *
        396 * @param {goog.html.TrustedResourceUrl=} opt_src The value of the src
        397 * attribute. If null or undefined src will not be set.
        398 * @param {goog.html.SafeHtml=} opt_srcdoc The value of the srcdoc attribute.
        399 * If null or undefined srcdoc will not be set.
        400 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
        401 * opt_attributes Mapping from attribute names to their values. Only
        402 * attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
        403 * undefined causes the attribute to be omitted.
        404 * @param {!goog.html.SafeHtml.TextOrHtml_|
        405 * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
        406 * HTML-escape and put inside the tag. Array elements are concatenated.
        407 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
        408 * @throws {Error} If invalid tag name, attribute name, or attribute value is
        409 * provided. If opt_attributes contains the src or srcdoc attributes.
        410 */
        411goog.html.SafeHtml.createIframe = function(
        412 opt_src, opt_srcdoc, opt_attributes, opt_content) {
        413 var fixedAttributes = {};
        414 fixedAttributes['src'] = opt_src || null;
        415 fixedAttributes['srcdoc'] = opt_srcdoc || null;
        416 var defaultAttributes = {'sandbox': ''};
        417 var attributes = goog.html.SafeHtml.combineAttributes(
        418 fixedAttributes, defaultAttributes, opt_attributes);
        419 return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
        420 'iframe', attributes, opt_content);
        421};
        422
        423
        424/**
        425 * Creates a SafeHtml representing a style tag. The type attribute is set
        426 * to "text/css".
        427 * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
        428 * styleSheet Content to put inside the tag. Array elements are
        429 * concatenated.
        430 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
        431 * opt_attributes Mapping from attribute names to their values. Only
        432 * attribute names consisting of [a-zA-Z0-9-] are allowed. Value of null or
        433 * undefined causes the attribute to be omitted.
        434 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
        435 * @throws {Error} If invalid attribute name or attribute value is provided. If
        436 * opt_attributes contains the type attribute.
        437 */
        438goog.html.SafeHtml.createStyle = function(styleSheet, opt_attributes) {
        439 var fixedAttributes = {'type': 'text/css'};
        440 var defaultAttributes = {};
        441 var attributes = goog.html.SafeHtml.combineAttributes(
        442 fixedAttributes, defaultAttributes, opt_attributes);
        443
        444 var content = '';
        445 styleSheet = goog.array.concat(styleSheet);
        446 for (var i = 0; i < styleSheet.length; i++) {
        447 content += goog.html.SafeStyleSheet.unwrap(styleSheet[i]);
        448 }
        449 // Convert to SafeHtml so that it's not HTML-escaped.
        450 var htmlContent = goog.html.SafeHtml
        451 .createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        452 content, goog.i18n.bidi.Dir.NEUTRAL);
        453 return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
        454 'style', attributes, htmlContent);
        455};
        456
        457
        458/**
        459 * @param {string} tagName The tag name.
        460 * @param {string} name The attribute name.
        461 * @param {!goog.html.SafeHtml.AttributeValue_} value The attribute value.
        462 * @return {string} A "name=value" string.
        463 * @throws {Error} If attribute value is unsafe for the given tag and attribute.
        464 * @private
        465 */
        466goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {
        467 // If it's goog.string.Const, allow any valid attribute name.
        468 if (value instanceof goog.string.Const) {
        469 value = goog.string.Const.unwrap(value);
        470 } else if (name.toLowerCase() == 'style') {
        471 value = goog.html.SafeHtml.getStyleValue_(value);
        472 } else if (/^on/i.test(name)) {
        473 // TODO(jakubvrana): Disallow more attributes with a special meaning.
        474 throw Error('Attribute "' + name +
        475 '" requires goog.string.Const value, "' + value + '" given.');
        476 // URL attributes handled differently accroding to tag.
        477 } else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {
        478 if (value instanceof goog.html.TrustedResourceUrl) {
        479 value = goog.html.TrustedResourceUrl.unwrap(value);
        480 } else if (value instanceof goog.html.SafeUrl) {
        481 value = goog.html.SafeUrl.unwrap(value);
        482 } else if (goog.isString(value)) {
        483 value = goog.html.SafeUrl.sanitize(value).getTypedStringValue();
        484 } else {
        485 throw Error('Attribute "' + name + '" on tag "' + tagName +
        486 '" requires goog.html.SafeUrl, goog.string.Const, or string,' +
        487 ' value "' + value + '" given.');
        488 }
        489 }
        490
        491 // Accept SafeUrl, TrustedResourceUrl, etc. for attributes which only require
        492 // HTML-escaping.
        493 if (value.implementsGoogStringTypedString) {
        494 // Ok to call getTypedStringValue() since there's no reliance on the type
        495 // contract for security here.
        496 value = value.getTypedStringValue();
        497 }
        498
        499 goog.asserts.assert(goog.isString(value) || goog.isNumber(value),
        500 'String or number value expected, got ' +
        501 (typeof value) + ' with value: ' + value);
        502 return name + '="' + goog.string.htmlEscape(String(value)) + '"';
        503};
        504
        505
        506/**
        507 * Gets value allowed in "style" attribute.
        508 * @param {goog.html.SafeHtml.AttributeValue_} value It could be SafeStyle or a
        509 * map which will be passed to goog.html.SafeStyle.create.
        510 * @return {string} Unwrapped value.
        511 * @throws {Error} If string value is given.
        512 * @private
        513 */
        514goog.html.SafeHtml.getStyleValue_ = function(value) {
        515 if (!goog.isObject(value)) {
        516 throw Error('The "style" attribute requires goog.html.SafeStyle or map ' +
        517 'of style properties, ' + (typeof value) + ' given: ' + value);
        518 }
        519 if (!(value instanceof goog.html.SafeStyle)) {
        520 // Process the property bag into a style object.
        521 value = goog.html.SafeStyle.create(value);
        522 }
        523 return goog.html.SafeStyle.unwrap(value);
        524};
        525
        526
        527/**
        528 * Creates a SafeHtml content with known directionality consisting of a tag with
        529 * optional attributes and optional content.
        530 * @param {!goog.i18n.bidi.Dir} dir Directionality.
        531 * @param {string} tagName
        532 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=} opt_attributes
        533 * @param {!goog.html.SafeHtml.TextOrHtml_|
        534 * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content
        535 * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
        536 */
        537goog.html.SafeHtml.createWithDir = function(dir, tagName, opt_attributes,
        538 opt_content) {
        539 var html = goog.html.SafeHtml.create(tagName, opt_attributes, opt_content);
        540 html.dir_ = dir;
        541 return html;
        542};
        543
        544
        545/**
        546 * Creates a new SafeHtml object by concatenating values.
        547 * @param {...(!goog.html.SafeHtml.TextOrHtml_|
        548 * !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Values to concatenate.
        549 * @return {!goog.html.SafeHtml}
        550 */
        551goog.html.SafeHtml.concat = function(var_args) {
        552 var dir = goog.i18n.bidi.Dir.NEUTRAL;
        553 var content = '';
        554
        555 /**
        556 * @param {!goog.html.SafeHtml.TextOrHtml_|
        557 * !Array<!goog.html.SafeHtml.TextOrHtml_>} argument
        558 */
        559 var addArgument = function(argument) {
        560 if (goog.isArray(argument)) {
        561 goog.array.forEach(argument, addArgument);
        562 } else {
        563 var html = goog.html.SafeHtml.htmlEscape(argument);
        564 content += goog.html.SafeHtml.unwrap(html);
        565 var htmlDir = html.getDirection();
        566 if (dir == goog.i18n.bidi.Dir.NEUTRAL) {
        567 dir = htmlDir;
        568 } else if (htmlDir != goog.i18n.bidi.Dir.NEUTRAL && dir != htmlDir) {
        569 dir = null;
        570 }
        571 }
        572 };
        573
        574 goog.array.forEach(arguments, addArgument);
        575 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        576 content, dir);
        577};
        578
        579
        580/**
        581 * Creates a new SafeHtml object with known directionality by concatenating the
        582 * values.
        583 * @param {!goog.i18n.bidi.Dir} dir Directionality.
        584 * @param {...(!goog.html.SafeHtml.TextOrHtml_|
        585 * !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Elements of array
        586 * arguments would be processed recursively.
        587 * @return {!goog.html.SafeHtml}
        588 */
        589goog.html.SafeHtml.concatWithDir = function(dir, var_args) {
        590 var html = goog.html.SafeHtml.concat(goog.array.slice(arguments, 1));
        591 html.dir_ = dir;
        592 return html;
        593};
        594
        595
        596/**
        597 * Type marker for the SafeHtml type, used to implement additional run-time
        598 * type checking.
        599 * @const {!Object}
        600 * @private
        601 */
        602goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
        603
        604
        605/**
        606 * Package-internal utility method to create SafeHtml instances.
        607 *
        608 * @param {string} html The string to initialize the SafeHtml object with.
        609 * @param {?goog.i18n.bidi.Dir} dir The directionality of the SafeHtml to be
        610 * constructed, or null if unknown.
        611 * @return {!goog.html.SafeHtml} The initialized SafeHtml object.
        612 * @package
        613 */
        614goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse = function(
        615 html, dir) {
        616 return new goog.html.SafeHtml().initSecurityPrivateDoNotAccessOrElse_(
        617 html, dir);
        618};
        619
        620
        621/**
        622 * Called from createSafeHtmlSecurityPrivateDoNotAccessOrElse(). This
        623 * method exists only so that the compiler can dead code eliminate static
        624 * fields (like EMPTY) when they're not accessed.
        625 * @param {string} html
        626 * @param {?goog.i18n.bidi.Dir} dir
        627 * @return {!goog.html.SafeHtml}
        628 * @private
        629 */
        630goog.html.SafeHtml.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
        631 html, dir) {
        632 this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = html;
        633 this.dir_ = dir;
        634 return this;
        635};
        636
        637
        638/**
        639 * Like create() but does not restrict which tags can be constructed.
        640 *
        641 * @param {string} tagName Tag name. Set or validated by caller.
        642 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=} opt_attributes
        643 * @param {(!goog.html.SafeHtml.TextOrHtml_|
        644 * !Array<!goog.html.SafeHtml.TextOrHtml_>)=} opt_content
        645 * @return {!goog.html.SafeHtml}
        646 * @throws {Error} If invalid or unsafe attribute name or value is provided.
        647 * @throws {goog.asserts.AssertionError} If content for void tag is provided.
        648 * @package
        649 */
        650goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse =
        651 function(tagName, opt_attributes, opt_content) {
        652 var dir = null;
        653 var result = '<' + tagName;
        654
        655 if (opt_attributes) {
        656 for (var name in opt_attributes) {
        657 if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(name)) {
        658 throw Error('Invalid attribute name "' + name + '".');
        659 }
        660 var value = opt_attributes[name];
        661 if (!goog.isDefAndNotNull(value)) {
        662 continue;
        663 }
        664 result += ' ' +
        665 goog.html.SafeHtml.getAttrNameAndValue_(tagName, name, value);
        666 }
        667 }
        668
        669 var content = opt_content;
        670 if (!goog.isDefAndNotNull(content)) {
        671 content = [];
        672 } else if (!goog.isArray(content)) {
        673 content = [content];
        674 }
        675
        676 if (goog.dom.tags.isVoidTag(tagName.toLowerCase())) {
        677 goog.asserts.assert(!content.length,
        678 'Void tag <' + tagName + '> does not allow content.');
        679 result += '>';
        680 } else {
        681 var html = goog.html.SafeHtml.concat(content);
        682 result += '>' + goog.html.SafeHtml.unwrap(html) + '</' + tagName + '>';
        683 dir = html.getDirection();
        684 }
        685
        686 var dirAttribute = opt_attributes && opt_attributes['dir'];
        687 if (dirAttribute) {
        688 if (/^(ltr|rtl|auto)$/i.test(dirAttribute)) {
        689 // If the tag has the "dir" attribute specified then its direction is
        690 // neutral because it can be safely used in any context.
        691 dir = goog.i18n.bidi.Dir.NEUTRAL;
        692 } else {
        693 dir = null;
        694 }
        695 }
        696
        697 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        698 result, dir);
        699};
        700
        701
        702/**
        703 * @param {!Object<string, string>} fixedAttributes
        704 * @param {!Object<string, string>} defaultAttributes
        705 * @param {!Object<string, goog.html.SafeHtml.AttributeValue_>=}
        706 * opt_attributes Optional attributes passed to create*().
        707 * @return {!Object<string, goog.html.SafeHtml.AttributeValue_>}
        708 * @throws {Error} If opt_attributes contains an attribute with the same name
        709 * as an attribute in fixedAttributes.
        710 * @package
        711 */
        712goog.html.SafeHtml.combineAttributes = function(
        713 fixedAttributes, defaultAttributes, opt_attributes) {
        714 var combinedAttributes = {};
        715 var name;
        716
        717 for (name in fixedAttributes) {
        718 goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
        719 combinedAttributes[name] = fixedAttributes[name];
        720 }
        721 for (name in defaultAttributes) {
        722 goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
        723 combinedAttributes[name] = defaultAttributes[name];
        724 }
        725
        726 for (name in opt_attributes) {
        727 var nameLower = name.toLowerCase();
        728 if (nameLower in fixedAttributes) {
        729 throw Error('Cannot override "' + nameLower + '" attribute, got "' +
        730 name + '" with value "' + opt_attributes[name] + '"');
        731 }
        732 if (nameLower in defaultAttributes) {
        733 delete combinedAttributes[nameLower];
        734 }
        735 combinedAttributes[name] = opt_attributes[name];
        736 }
        737
        738 return combinedAttributes;
        739};
        740
        741
        742/**
        743 * A SafeHtml instance corresponding to the HTML doctype: "<!DOCTYPE html>".
        744 * @const {!goog.html.SafeHtml}
        745 */
        746goog.html.SafeHtml.DOCTYPE_HTML =
        747 goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        748 '<!DOCTYPE html>', goog.i18n.bidi.Dir.NEUTRAL);
        749
        750
        751/**
        752 * A SafeHtml instance corresponding to the empty string.
        753 * @const {!goog.html.SafeHtml}
        754 */
        755goog.html.SafeHtml.EMPTY =
        756 goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        757 '', goog.i18n.bidi.Dir.NEUTRAL);
        \ No newline at end of file diff --git a/docs/source/lib/goog/html/safescript.js.src.html b/docs/source/lib/goog/html/safescript.js.src.html new file mode 100644 index 0000000..bf38421 --- /dev/null +++ b/docs/source/lib/goog/html/safescript.js.src.html @@ -0,0 +1 @@ +safescript.js

        lib/goog/html/safescript.js

        1// Copyright 2014 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 The SafeScript type and its builders.
        17 *
        18 * TODO(xtof): Link to document stating type contract.
        19 */
        20
        21goog.provide('goog.html.SafeScript');
        22
        23goog.require('goog.asserts');
        24goog.require('goog.string.Const');
        25goog.require('goog.string.TypedString');
        26
        27
        28
        29/**
        30 * A string-like object which represents JavaScript code and that carries the
        31 * security type contract that its value, as a string, will not cause execution
        32 * of unconstrained attacker controlled code (XSS) when evaluated as JavaScript
        33 * in a browser.
        34 *
        35 * Instances of this type must be created via the factory method
        36 * {@code goog.html.SafeScript.fromConstant} and not by invoking its
        37 * constructor. The constructor intentionally takes no parameters and the type
        38 * is immutable; hence only a default instance corresponding to the empty string
        39 * can be obtained via constructor invocation.
        40 *
        41 * A SafeScript's string representation can safely be interpolated as the
        42 * content of a script element within HTML. The SafeScript string should not be
        43 * escaped before interpolation.
        44 *
        45 * Note that the SafeScript might contain text that is attacker-controlled but
        46 * that text should have been interpolated with appropriate escaping,
        47 * sanitization and/or validation into the right location in the script, such
        48 * that it is highly constrained in its effect (for example, it had to match a
        49 * set of whitelisted words).
        50 *
        51 * A SafeScript can be constructed via security-reviewed unchecked
        52 * conversions. In this case producers of SafeScript must ensure themselves that
        53 * the SafeScript does not contain unsafe script. Note in particular that
        54 * {@code &lt;} is dangerous, even when inside JavaScript strings, and so should
        55 * always be forbidden or JavaScript escaped in user controlled input. For
        56 * example, if {@code &lt;/script&gt;&lt;script&gt;evil&lt;/script&gt;"} were
        57 * interpolated inside a JavaScript string, it would break out of the context
        58 * of the original script element and {@code evil} would execute. Also note
        59 * that within an HTML script (raw text) element, HTML character references,
        60 * such as "&lt;" are not allowed. See
        61 * http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements.
        62 *
        63 * @see goog.html.SafeScript#fromConstant
        64 * @constructor
        65 * @final
        66 * @struct
        67 * @implements {goog.string.TypedString}
        68 */
        69goog.html.SafeScript = function() {
        70 /**
        71 * The contained value of this SafeScript. The field has a purposely
        72 * ugly name to make (non-compiled) code that attempts to directly access this
        73 * field stand out.
        74 * @private {string}
        75 */
        76 this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = '';
        77
        78 /**
        79 * A type marker used to implement additional run-time type checking.
        80 * @see goog.html.SafeScript#unwrap
        81 * @const
        82 * @private
        83 */
        84 this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
        85 goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
        86};
        87
        88
        89/**
        90 * @override
        91 * @const
        92 */
        93goog.html.SafeScript.prototype.implementsGoogStringTypedString = true;
        94
        95
        96/**
        97 * Type marker for the SafeScript type, used to implement additional
        98 * run-time type checking.
        99 * @const {!Object}
        100 * @private
        101 */
        102goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
        103
        104
        105/**
        106 * Creates a SafeScript object from a compile-time constant string.
        107 *
        108 * @param {!goog.string.Const} script A compile-time-constant string from which
        109 * to create a SafeScript.
        110 * @return {!goog.html.SafeScript} A SafeScript object initialized to
        111 * {@code script}.
        112 */
        113goog.html.SafeScript.fromConstant = function(script) {
        114 var scriptString = goog.string.Const.unwrap(script);
        115 if (scriptString.length === 0) {
        116 return goog.html.SafeScript.EMPTY;
        117 }
        118 return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
        119 scriptString);
        120};
        121
        122
        123/**
        124 * Returns this SafeScript's value as a string.
        125 *
        126 * IMPORTANT: In code where it is security relevant that an object's type is
        127 * indeed {@code SafeScript}, use {@code goog.html.SafeScript.unwrap} instead of
        128 * this method. If in doubt, assume that it's security relevant. In particular,
        129 * note that goog.html functions which return a goog.html type do not guarantee
        130 * the returned instance is of the right type. For example:
        131 *
        132 * <pre>
        133 * var fakeSafeHtml = new String('fake');
        134 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
        135 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
        136 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
        137 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
        138 * // instanceof goog.html.SafeHtml.
        139 * </pre>
        140 *
        141 * @see goog.html.SafeScript#unwrap
        142 * @override
        143 */
        144goog.html.SafeScript.prototype.getTypedStringValue = function() {
        145 return this.privateDoNotAccessOrElseSafeScriptWrappedValue_;
        146};
        147
        148
        149if (goog.DEBUG) {
        150 /**
        151 * Returns a debug string-representation of this value.
        152 *
        153 * To obtain the actual string value wrapped in a SafeScript, use
        154 * {@code goog.html.SafeScript.unwrap}.
        155 *
        156 * @see goog.html.SafeScript#unwrap
        157 * @override
        158 */
        159 goog.html.SafeScript.prototype.toString = function() {
        160 return 'SafeScript{' +
        161 this.privateDoNotAccessOrElseSafeScriptWrappedValue_ + '}';
        162 };
        163}
        164
        165
        166/**
        167 * Performs a runtime check that the provided object is indeed a
        168 * SafeScript object, and returns its value.
        169 *
        170 * @param {!goog.html.SafeScript} safeScript The object to extract from.
        171 * @return {string} The safeScript object's contained string, unless
        172 * the run-time type check fails. In that case, {@code unwrap} returns an
        173 * innocuous string, or, if assertions are enabled, throws
        174 * {@code goog.asserts.AssertionError}.
        175 */
        176goog.html.SafeScript.unwrap = function(safeScript) {
        177 // Perform additional Run-time type-checking to ensure that
        178 // safeScript is indeed an instance of the expected type. This
        179 // provides some additional protection against security bugs due to
        180 // application code that disables type checks.
        181 // Specifically, the following checks are performed:
        182 // 1. The object is an instance of the expected type.
        183 // 2. The object is not an instance of a subclass.
        184 // 3. The object carries a type marker for the expected type. "Faking" an
        185 // object requires a reference to the type marker, which has names intended
        186 // to stand out in code reviews.
        187 if (safeScript instanceof goog.html.SafeScript &&
        188 safeScript.constructor === goog.html.SafeScript &&
        189 safeScript.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
        190 goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
        191 return safeScript.privateDoNotAccessOrElseSafeScriptWrappedValue_;
        192 } else {
        193 goog.asserts.fail(
        194 'expected object of type SafeScript, got \'' + safeScript + '\'');
        195 return 'type_error:SafeScript';
        196 }
        197};
        198
        199
        200/**
        201 * Package-internal utility method to create SafeScript instances.
        202 *
        203 * @param {string} script The string to initialize the SafeScript object with.
        204 * @return {!goog.html.SafeScript} The initialized SafeScript object.
        205 * @package
        206 */
        207goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse =
        208 function(script) {
        209 return new goog.html.SafeScript().initSecurityPrivateDoNotAccessOrElse_(
        210 script);
        211};
        212
        213
        214/**
        215 * Called from createSafeScriptSecurityPrivateDoNotAccessOrElse(). This
        216 * method exists only so that the compiler can dead code eliminate static
        217 * fields (like EMPTY) when they're not accessed.
        218 * @param {string} script
        219 * @return {!goog.html.SafeScript}
        220 * @private
        221 */
        222goog.html.SafeScript.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
        223 script) {
        224 this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = script;
        225 return this;
        226};
        227
        228
        229/**
        230 * A SafeScript instance corresponding to the empty string.
        231 * @const {!goog.html.SafeScript}
        232 */
        233goog.html.SafeScript.EMPTY =
        234 goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse('');
        \ No newline at end of file diff --git a/docs/source/lib/goog/html/safestyle.js.src.html b/docs/source/lib/goog/html/safestyle.js.src.html new file mode 100644 index 0000000..8fcf043 --- /dev/null +++ b/docs/source/lib/goog/html/safestyle.js.src.html @@ -0,0 +1 @@ +safestyle.js

        lib/goog/html/safestyle.js

        1// Copyright 2014 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 The SafeStyle type and its builders.
        17 *
        18 * TODO(xtof): Link to document stating type contract.
        19 */
        20
        21goog.provide('goog.html.SafeStyle');
        22
        23goog.require('goog.array');
        24goog.require('goog.asserts');
        25goog.require('goog.string');
        26goog.require('goog.string.Const');
        27goog.require('goog.string.TypedString');
        28
        29
        30
        31/**
        32 * A string-like object which represents a sequence of CSS declarations
        33 * ({@code propertyName1: propertyvalue1; propertyName2: propertyValue2; ...})
        34 * and that carries the security type contract that its value, as a string,
        35 * will not cause untrusted script execution (XSS) when evaluated as CSS in a
        36 * browser.
        37 *
        38 * Instances of this type must be created via the factory methods
        39 * ({@code goog.html.SafeStyle.create} or
        40 * {@code goog.html.SafeStyle.fromConstant}) and not by invoking its
        41 * constructor. The constructor intentionally takes no parameters and the type
        42 * is immutable; hence only a default instance corresponding to the empty string
        43 * can be obtained via constructor invocation.
        44 *
        45 * A SafeStyle's string representation ({@link #getTypedStringValue()}) can
        46 * safely:
        47 * <ul>
        48 * <li>Be interpolated as the entire content of a *quoted* HTML style
        49 * attribute, or before already existing properties. The SafeStyle string
        50 * *must be HTML-attribute-escaped* (where " and ' are escaped) before
        51 * interpolation.
        52 * <li>Be interpolated as the entire content of a {}-wrapped block within a
        53 * stylesheet, or before already existing properties. The SafeStyle string
        54 * should not be escaped before interpolation. SafeStyle's contract also
        55 * guarantees that the string will not be able to introduce new properties
        56 * or elide existing ones.
        57 * <li>Be assigned to the style property of a DOM node. The SafeStyle string
        58 * should not be escaped before being assigned to the property.
        59 * </ul>
        60 *
        61 * A SafeStyle may never contain literal angle brackets. Otherwise, it could
        62 * be unsafe to place a SafeStyle into a &lt;style&gt; tag (where it can't
        63 * be HTML escaped). For example, if the SafeStyle containing
        64 * "{@code font: 'foo &lt;style/&gt;&lt;script&gt;evil&lt;/script&gt;'}" were
        65 * interpolated within a &lt;style&gt; tag, this would then break out of the
        66 * style context into HTML.
        67 *
        68 * A SafeStyle may contain literal single or double quotes, and as such the
        69 * entire style string must be escaped when used in a style attribute (if
        70 * this were not the case, the string could contain a matching quote that
        71 * would escape from the style attribute).
        72 *
        73 * Values of this type must be composable, i.e. for any two values
        74 * {@code style1} and {@code style2} of this type,
        75 * {@code goog.html.SafeStyle.unwrap(style1) +
        76 * goog.html.SafeStyle.unwrap(style2)} must itself be a value that satisfies
        77 * the SafeStyle type constraint. This requirement implies that for any value
        78 * {@code style} of this type, {@code goog.html.SafeStyle.unwrap(style)} must
        79 * not end in a "property value" or "property name" context. For example,
        80 * a value of {@code background:url("} or {@code font-} would not satisfy the
        81 * SafeStyle contract. This is because concatenating such strings with a
        82 * second value that itself does not contain unsafe CSS can result in an
        83 * overall string that does. For example, if {@code javascript:evil())"} is
        84 * appended to {@code background:url("}, the resulting string may result in
        85 * the execution of a malicious script.
        86 *
        87 * TODO(user): Consider whether we should implement UTF-8 interchange
        88 * validity checks and blacklisting of newlines (including Unicode ones) and
        89 * other whitespace characters (\t, \f). Document here if so and also update
        90 * SafeStyle.fromConstant().
        91 *
        92 * The following example values comply with this type's contract:
        93 * <ul>
        94 * <li><pre>width: 1em;</pre>
        95 * <li><pre>height:1em;</pre>
        96 * <li><pre>width: 1em;height: 1em;</pre>
        97 * <li><pre>background:url('http://url');</pre>
        98 * </ul>
        99 * In addition, the empty string is safe for use in a CSS attribute.
        100 *
        101 * The following example values do NOT comply with this type's contract:
        102 * <ul>
        103 * <li><pre>background: red</pre> (missing a trailing semi-colon)
        104 * <li><pre>background:</pre> (missing a value and a trailing semi-colon)
        105 * <li><pre>1em</pre> (missing an attribute name, which provides context for
        106 * the value)
        107 * </ul>
        108 *
        109 * @see goog.html.SafeStyle#create
        110 * @see goog.html.SafeStyle#fromConstant
        111 * @see http://www.w3.org/TR/css3-syntax/
        112 * @constructor
        113 * @final
        114 * @struct
        115 * @implements {goog.string.TypedString}
        116 */
        117goog.html.SafeStyle = function() {
        118 /**
        119 * The contained value of this SafeStyle. The field has a purposely
        120 * ugly name to make (non-compiled) code that attempts to directly access this
        121 * field stand out.
        122 * @private {string}
        123 */
        124 this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = '';
        125
        126 /**
        127 * A type marker used to implement additional run-time type checking.
        128 * @see goog.html.SafeStyle#unwrap
        129 * @const
        130 * @private
        131 */
        132 this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
        133 goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
        134};
        135
        136
        137/**
        138 * @override
        139 * @const
        140 */
        141goog.html.SafeStyle.prototype.implementsGoogStringTypedString = true;
        142
        143
        144/**
        145 * Type marker for the SafeStyle type, used to implement additional
        146 * run-time type checking.
        147 * @const {!Object}
        148 * @private
        149 */
        150goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
        151
        152
        153/**
        154 * Creates a SafeStyle object from a compile-time constant string.
        155 *
        156 * {@code style} should be in the format
        157 * {@code name: value; [name: value; ...]} and must not have any < or >
        158 * characters in it. This is so that SafeStyle's contract is preserved,
        159 * allowing the SafeStyle to correctly be interpreted as a sequence of CSS
        160 * declarations and without affecting the syntactic structure of any
        161 * surrounding CSS and HTML.
        162 *
        163 * This method performs basic sanity checks on the format of {@code style}
        164 * but does not constrain the format of {@code name} and {@code value}, except
        165 * for disallowing tag characters.
        166 *
        167 * @param {!goog.string.Const} style A compile-time-constant string from which
        168 * to create a SafeStyle.
        169 * @return {!goog.html.SafeStyle} A SafeStyle object initialized to
        170 * {@code style}.
        171 */
        172goog.html.SafeStyle.fromConstant = function(style) {
        173 var styleString = goog.string.Const.unwrap(style);
        174 if (styleString.length === 0) {
        175 return goog.html.SafeStyle.EMPTY;
        176 }
        177 goog.html.SafeStyle.checkStyle_(styleString);
        178 goog.asserts.assert(goog.string.endsWith(styleString, ';'),
        179 'Last character of style string is not \';\': ' + styleString);
        180 goog.asserts.assert(goog.string.contains(styleString, ':'),
        181 'Style string must contain at least one \':\', to ' +
        182 'specify a "name: value" pair: ' + styleString);
        183 return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
        184 styleString);
        185};
        186
        187
        188/**
        189 * Checks if the style definition is valid.
        190 * @param {string} style
        191 * @private
        192 */
        193goog.html.SafeStyle.checkStyle_ = function(style) {
        194 goog.asserts.assert(!/[<>]/.test(style),
        195 'Forbidden characters in style string: ' + style);
        196};
        197
        198
        199/**
        200 * Returns this SafeStyle's value as a string.
        201 *
        202 * IMPORTANT: In code where it is security relevant that an object's type is
        203 * indeed {@code SafeStyle}, use {@code goog.html.SafeStyle.unwrap} instead of
        204 * this method. If in doubt, assume that it's security relevant. In particular,
        205 * note that goog.html functions which return a goog.html type do not guarantee
        206 * the returned instance is of the right type. For example:
        207 *
        208 * <pre>
        209 * var fakeSafeHtml = new String('fake');
        210 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
        211 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
        212 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
        213 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
        214 * // instanceof goog.html.SafeHtml.
        215 * </pre>
        216 *
        217 * @see goog.html.SafeStyle#unwrap
        218 * @override
        219 */
        220goog.html.SafeStyle.prototype.getTypedStringValue = function() {
        221 return this.privateDoNotAccessOrElseSafeStyleWrappedValue_;
        222};
        223
        224
        225if (goog.DEBUG) {
        226 /**
        227 * Returns a debug string-representation of this value.
        228 *
        229 * To obtain the actual string value wrapped in a SafeStyle, use
        230 * {@code goog.html.SafeStyle.unwrap}.
        231 *
        232 * @see goog.html.SafeStyle#unwrap
        233 * @override
        234 */
        235 goog.html.SafeStyle.prototype.toString = function() {
        236 return 'SafeStyle{' +
        237 this.privateDoNotAccessOrElseSafeStyleWrappedValue_ + '}';
        238 };
        239}
        240
        241
        242/**
        243 * Performs a runtime check that the provided object is indeed a
        244 * SafeStyle object, and returns its value.
        245 *
        246 * @param {!goog.html.SafeStyle} safeStyle The object to extract from.
        247 * @return {string} The safeStyle object's contained string, unless
        248 * the run-time type check fails. In that case, {@code unwrap} returns an
        249 * innocuous string, or, if assertions are enabled, throws
        250 * {@code goog.asserts.AssertionError}.
        251 */
        252goog.html.SafeStyle.unwrap = function(safeStyle) {
        253 // Perform additional Run-time type-checking to ensure that
        254 // safeStyle is indeed an instance of the expected type. This
        255 // provides some additional protection against security bugs due to
        256 // application code that disables type checks.
        257 // Specifically, the following checks are performed:
        258 // 1. The object is an instance of the expected type.
        259 // 2. The object is not an instance of a subclass.
        260 // 3. The object carries a type marker for the expected type. "Faking" an
        261 // object requires a reference to the type marker, which has names intended
        262 // to stand out in code reviews.
        263 if (safeStyle instanceof goog.html.SafeStyle &&
        264 safeStyle.constructor === goog.html.SafeStyle &&
        265 safeStyle.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
        266 goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
        267 return safeStyle.privateDoNotAccessOrElseSafeStyleWrappedValue_;
        268 } else {
        269 goog.asserts.fail(
        270 'expected object of type SafeStyle, got \'' + safeStyle + '\'');
        271 return 'type_error:SafeStyle';
        272 }
        273};
        274
        275
        276/**
        277 * Package-internal utility method to create SafeStyle instances.
        278 *
        279 * @param {string} style The string to initialize the SafeStyle object with.
        280 * @return {!goog.html.SafeStyle} The initialized SafeStyle object.
        281 * @package
        282 */
        283goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse =
        284 function(style) {
        285 return new goog.html.SafeStyle().initSecurityPrivateDoNotAccessOrElse_(style);
        286};
        287
        288
        289/**
        290 * Called from createSafeStyleSecurityPrivateDoNotAccessOrElse(). This
        291 * method exists only so that the compiler can dead code eliminate static
        292 * fields (like EMPTY) when they're not accessed.
        293 * @param {string} style
        294 * @return {!goog.html.SafeStyle}
        295 * @private
        296 */
        297goog.html.SafeStyle.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
        298 style) {
        299 this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = style;
        300 return this;
        301};
        302
        303
        304/**
        305 * A SafeStyle instance corresponding to the empty string.
        306 * @const {!goog.html.SafeStyle}
        307 */
        308goog.html.SafeStyle.EMPTY =
        309 goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse('');
        310
        311
        312/**
        313 * The innocuous string generated by goog.html.SafeUrl.create when passed
        314 * an unsafe value.
        315 * @const {string}
        316 */
        317goog.html.SafeStyle.INNOCUOUS_STRING = 'zClosurez';
        318
        319
        320/**
        321 * Mapping of property names to their values.
        322 * @typedef {!Object<string, goog.string.Const|string>}
        323 */
        324goog.html.SafeStyle.PropertyMap;
        325
        326
        327/**
        328 * Creates a new SafeStyle object from the properties specified in the map.
        329 * @param {goog.html.SafeStyle.PropertyMap} map Mapping of property names to
        330 * their values, for example {'margin': '1px'}. Names must consist of
        331 * [-_a-zA-Z0-9]. Values might be strings consisting of
        332 * [-,.'"%_!# a-zA-Z0-9], where " and ' must be properly balanced.
        333 * Other values must be wrapped in goog.string.Const. Null value causes
        334 * skipping the property.
        335 * @return {!goog.html.SafeStyle}
        336 * @throws {Error} If invalid name is provided.
        337 * @throws {goog.asserts.AssertionError} If invalid value is provided. With
        338 * disabled assertions, invalid value is replaced by
        339 * goog.html.SafeStyle.INNOCUOUS_STRING.
        340 */
        341goog.html.SafeStyle.create = function(map) {
        342 var style = '';
        343 for (var name in map) {
        344 if (!/^[-_a-zA-Z0-9]+$/.test(name)) {
        345 throw Error('Name allows only [-_a-zA-Z0-9], got: ' + name);
        346 }
        347 var value = map[name];
        348 if (value == null) {
        349 continue;
        350 }
        351 if (value instanceof goog.string.Const) {
        352 value = goog.string.Const.unwrap(value);
        353 // These characters can be used to change context and we don't want that
        354 // even with const values.
        355 goog.asserts.assert(!/[{;}]/.test(value), 'Value does not allow [{;}].');
        356 } else if (!goog.html.SafeStyle.VALUE_RE_.test(value)) {
        357 goog.asserts.fail(
        358 'String value allows only [-,."\'%_!# a-zA-Z0-9], got: ' + value);
        359 value = goog.html.SafeStyle.INNOCUOUS_STRING;
        360 } else if (!goog.html.SafeStyle.hasBalancedQuotes_(value)) {
        361 goog.asserts.fail('String value requires balanced quotes, got: ' + value);
        362 value = goog.html.SafeStyle.INNOCUOUS_STRING;
        363 }
        364 style += name + ':' + value + ';';
        365 }
        366 if (!style) {
        367 return goog.html.SafeStyle.EMPTY;
        368 }
        369 goog.html.SafeStyle.checkStyle_(style);
        370 return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
        371 style);
        372};
        373
        374
        375/**
        376 * Checks that quotes (" and ') are properly balanced inside a string. Assumes
        377 * that neither escape (\) nor any other character that could result in
        378 * breaking out of a string parsing context are allowed;
        379 * see http://www.w3.org/TR/css3-syntax/#string-token-diagram.
        380 * @param {string} value Untrusted CSS property value.
        381 * @return {boolean} True if property value is safe with respect to quote
        382 * balancedness.
        383 * @private
        384 */
        385goog.html.SafeStyle.hasBalancedQuotes_ = function(value) {
        386 var outsideSingle = true;
        387 var outsideDouble = true;
        388 for (var i = 0; i < value.length; i++) {
        389 var c = value.charAt(i);
        390 if (c == "'" && outsideDouble) {
        391 outsideSingle = !outsideSingle;
        392 } else if (c == '"' && outsideSingle) {
        393 outsideDouble = !outsideDouble;
        394 }
        395 }
        396 return outsideSingle && outsideDouble;
        397};
        398
        399
        400// Keep in sync with the error string in create().
        401/**
        402 * Regular expression for safe values.
        403 *
        404 * Quotes (" and ') are allowed, but a check must be done elsewhere to ensure
        405 * they're balanced.
        406 *
        407 * ',' allows multiple values to be assigned to the same property
        408 * (e.g. background-attachment or font-family) and hence could allow
        409 * multiple values to get injected, but that should pose no risk of XSS.
        410 * @const {!RegExp}
        411 * @private
        412 */
        413goog.html.SafeStyle.VALUE_RE_ = /^[-,."'%_!# a-zA-Z0-9]+$/;
        414
        415
        416/**
        417 * Creates a new SafeStyle object by concatenating the values.
        418 * @param {...(!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>)} var_args
        419 * SafeStyles to concatenate.
        420 * @return {!goog.html.SafeStyle}
        421 */
        422goog.html.SafeStyle.concat = function(var_args) {
        423 var style = '';
        424
        425 /**
        426 * @param {!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>} argument
        427 */
        428 var addArgument = function(argument) {
        429 if (goog.isArray(argument)) {
        430 goog.array.forEach(argument, addArgument);
        431 } else {
        432 style += goog.html.SafeStyle.unwrap(argument);
        433 }
        434 };
        435
        436 goog.array.forEach(arguments, addArgument);
        437 if (!style) {
        438 return goog.html.SafeStyle.EMPTY;
        439 }
        440 return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
        441 style);
        442};
        \ No newline at end of file diff --git a/docs/source/lib/goog/html/safestylesheet.js.src.html b/docs/source/lib/goog/html/safestylesheet.js.src.html new file mode 100644 index 0000000..ebf132e --- /dev/null +++ b/docs/source/lib/goog/html/safestylesheet.js.src.html @@ -0,0 +1 @@ +safestylesheet.js

        lib/goog/html/safestylesheet.js

        1// Copyright 2014 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 The SafeStyleSheet type and its builders.
        17 *
        18 * TODO(xtof): Link to document stating type contract.
        19 */
        20
        21goog.provide('goog.html.SafeStyleSheet');
        22
        23goog.require('goog.array');
        24goog.require('goog.asserts');
        25goog.require('goog.string');
        26goog.require('goog.string.Const');
        27goog.require('goog.string.TypedString');
        28
        29
        30
        31/**
        32 * A string-like object which represents a CSS style sheet and that carries the
        33 * security type contract that its value, as a string, will not cause untrusted
        34 * script execution (XSS) when evaluated as CSS in a browser.
        35 *
        36 * Instances of this type must be created via the factory method
        37 * {@code goog.html.SafeStyleSheet.fromConstant} and not by invoking its
        38 * constructor. The constructor intentionally takes no parameters and the type
        39 * is immutable; hence only a default instance corresponding to the empty string
        40 * can be obtained via constructor invocation.
        41 *
        42 * A SafeStyleSheet's string representation can safely be interpolated as the
        43 * content of a style element within HTML. The SafeStyleSheet string should
        44 * not be escaped before interpolation.
        45 *
        46 * Values of this type must be composable, i.e. for any two values
        47 * {@code styleSheet1} and {@code styleSheet2} of this type,
        48 * {@code goog.html.SafeStyleSheet.unwrap(styleSheet1) +
        49 * goog.html.SafeStyleSheet.unwrap(styleSheet2)} must itself be a value that
        50 * satisfies the SafeStyleSheet type constraint. This requirement implies that
        51 * for any value {@code styleSheet} of this type,
        52 * {@code goog.html.SafeStyleSheet.unwrap(styleSheet1)} must end in
        53 * "beginning of rule" context.
        54
        55 * A SafeStyleSheet can be constructed via security-reviewed unchecked
        56 * conversions. In this case producers of SafeStyleSheet must ensure themselves
        57 * that the SafeStyleSheet does not contain unsafe script. Note in particular
        58 * that {@code &lt;} is dangerous, even when inside CSS strings, and so should
        59 * always be forbidden or CSS-escaped in user controlled input. For example, if
        60 * {@code &lt;/style&gt;&lt;script&gt;evil&lt;/script&gt;"} were interpolated
        61 * inside a CSS string, it would break out of the context of the original
        62 * style element and {@code evil} would execute. Also note that within an HTML
        63 * style (raw text) element, HTML character references, such as
        64 * {@code &amp;lt;}, are not allowed. See
        65 * http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements
        66 * (similar considerations apply to the style element).
        67 *
        68 * @see goog.html.SafeStyleSheet#fromConstant
        69 * @constructor
        70 * @final
        71 * @struct
        72 * @implements {goog.string.TypedString}
        73 */
        74goog.html.SafeStyleSheet = function() {
        75 /**
        76 * The contained value of this SafeStyleSheet. The field has a purposely
        77 * ugly name to make (non-compiled) code that attempts to directly access this
        78 * field stand out.
        79 * @private {string}
        80 */
        81 this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = '';
        82
        83 /**
        84 * A type marker used to implement additional run-time type checking.
        85 * @see goog.html.SafeStyleSheet#unwrap
        86 * @const
        87 * @private
        88 */
        89 this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
        90 goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
        91};
        92
        93
        94/**
        95 * @override
        96 * @const
        97 */
        98goog.html.SafeStyleSheet.prototype.implementsGoogStringTypedString = true;
        99
        100
        101/**
        102 * Type marker for the SafeStyleSheet type, used to implement additional
        103 * run-time type checking.
        104 * @const {!Object}
        105 * @private
        106 */
        107goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
        108
        109
        110/**
        111 * Creates a new SafeStyleSheet object by concatenating values.
        112 * @param {...(!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>)}
        113 * var_args Values to concatenate.
        114 * @return {!goog.html.SafeStyleSheet}
        115 */
        116goog.html.SafeStyleSheet.concat = function(var_args) {
        117 var result = '';
        118
        119 /**
        120 * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
        121 * argument
        122 */
        123 var addArgument = function(argument) {
        124 if (goog.isArray(argument)) {
        125 goog.array.forEach(argument, addArgument);
        126 } else {
        127 result += goog.html.SafeStyleSheet.unwrap(argument);
        128 }
        129 };
        130
        131 goog.array.forEach(arguments, addArgument);
        132 return goog.html.SafeStyleSheet
        133 .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(result);
        134};
        135
        136
        137/**
        138 * Creates a SafeStyleSheet object from a compile-time constant string.
        139 *
        140 * {@code styleSheet} must not have any &lt; characters in it, so that
        141 * the syntactic structure of the surrounding HTML is not affected.
        142 *
        143 * @param {!goog.string.Const} styleSheet A compile-time-constant string from
        144 * which to create a SafeStyleSheet.
        145 * @return {!goog.html.SafeStyleSheet} A SafeStyleSheet object initialized to
        146 * {@code styleSheet}.
        147 */
        148goog.html.SafeStyleSheet.fromConstant = function(styleSheet) {
        149 var styleSheetString = goog.string.Const.unwrap(styleSheet);
        150 if (styleSheetString.length === 0) {
        151 return goog.html.SafeStyleSheet.EMPTY;
        152 }
        153 // > is a valid character in CSS selectors and there's no strict need to
        154 // block it if we already block <.
        155 goog.asserts.assert(!goog.string.contains(styleSheetString, '<'),
        156 "Forbidden '<' character in style sheet string: " + styleSheetString);
        157 return goog.html.SafeStyleSheet.
        158 createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheetString);
        159};
        160
        161
        162/**
        163 * Returns this SafeStyleSheet's value as a string.
        164 *
        165 * IMPORTANT: In code where it is security relevant that an object's type is
        166 * indeed {@code SafeStyleSheet}, use {@code goog.html.SafeStyleSheet.unwrap}
        167 * instead of this method. If in doubt, assume that it's security relevant. In
        168 * particular, note that goog.html functions which return a goog.html type do
        169 * not guarantee the returned instance is of the right type. For example:
        170 *
        171 * <pre>
        172 * var fakeSafeHtml = new String('fake');
        173 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
        174 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
        175 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
        176 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
        177 * // instanceof goog.html.SafeHtml.
        178 * </pre>
        179 *
        180 * @see goog.html.SafeStyleSheet#unwrap
        181 * @override
        182 */
        183goog.html.SafeStyleSheet.prototype.getTypedStringValue = function() {
        184 return this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
        185};
        186
        187
        188if (goog.DEBUG) {
        189 /**
        190 * Returns a debug string-representation of this value.
        191 *
        192 * To obtain the actual string value wrapped in a SafeStyleSheet, use
        193 * {@code goog.html.SafeStyleSheet.unwrap}.
        194 *
        195 * @see goog.html.SafeStyleSheet#unwrap
        196 * @override
        197 */
        198 goog.html.SafeStyleSheet.prototype.toString = function() {
        199 return 'SafeStyleSheet{' +
        200 this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ + '}';
        201 };
        202}
        203
        204
        205/**
        206 * Performs a runtime check that the provided object is indeed a
        207 * SafeStyleSheet object, and returns its value.
        208 *
        209 * @param {!goog.html.SafeStyleSheet} safeStyleSheet The object to extract from.
        210 * @return {string} The safeStyleSheet object's contained string, unless
        211 * the run-time type check fails. In that case, {@code unwrap} returns an
        212 * innocuous string, or, if assertions are enabled, throws
        213 * {@code goog.asserts.AssertionError}.
        214 */
        215goog.html.SafeStyleSheet.unwrap = function(safeStyleSheet) {
        216 // Perform additional Run-time type-checking to ensure that
        217 // safeStyleSheet is indeed an instance of the expected type. This
        218 // provides some additional protection against security bugs due to
        219 // application code that disables type checks.
        220 // Specifically, the following checks are performed:
        221 // 1. The object is an instance of the expected type.
        222 // 2. The object is not an instance of a subclass.
        223 // 3. The object carries a type marker for the expected type. "Faking" an
        224 // object requires a reference to the type marker, which has names intended
        225 // to stand out in code reviews.
        226 if (safeStyleSheet instanceof goog.html.SafeStyleSheet &&
        227 safeStyleSheet.constructor === goog.html.SafeStyleSheet &&
        228 safeStyleSheet.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
        229 goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
        230 return safeStyleSheet.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
        231 } else {
        232 goog.asserts.fail(
        233 "expected object of type SafeStyleSheet, got '" + safeStyleSheet +
        234 "'");
        235 return 'type_error:SafeStyleSheet';
        236 }
        237};
        238
        239
        240/**
        241 * Package-internal utility method to create SafeStyleSheet instances.
        242 *
        243 * @param {string} styleSheet The string to initialize the SafeStyleSheet
        244 * object with.
        245 * @return {!goog.html.SafeStyleSheet} The initialized SafeStyleSheet object.
        246 * @package
        247 */
        248goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse =
        249 function(styleSheet) {
        250 return new goog.html.SafeStyleSheet().initSecurityPrivateDoNotAccessOrElse_(
        251 styleSheet);
        252};
        253
        254
        255/**
        256 * Called from createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(). This
        257 * method exists only so that the compiler can dead code eliminate static
        258 * fields (like EMPTY) when they're not accessed.
        259 * @param {string} styleSheet
        260 * @return {!goog.html.SafeStyleSheet}
        261 * @private
        262 */
        263goog.html.SafeStyleSheet.prototype.initSecurityPrivateDoNotAccessOrElse_ =
        264 function(styleSheet) {
        265 this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = styleSheet;
        266 return this;
        267};
        268
        269
        270/**
        271 * A SafeStyleSheet instance corresponding to the empty string.
        272 * @const {!goog.html.SafeStyleSheet}
        273 */
        274goog.html.SafeStyleSheet.EMPTY =
        275 goog.html.SafeStyleSheet.
        276 createSafeStyleSheetSecurityPrivateDoNotAccessOrElse('');
        \ No newline at end of file diff --git a/docs/source/lib/goog/html/safeurl.js.src.html b/docs/source/lib/goog/html/safeurl.js.src.html new file mode 100644 index 0000000..35997f0 --- /dev/null +++ b/docs/source/lib/goog/html/safeurl.js.src.html @@ -0,0 +1 @@ +safeurl.js

        lib/goog/html/safeurl.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 The SafeUrl type and its builders.
        17 *
        18 * TODO(xtof): Link to document stating type contract.
        19 */
        20
        21goog.provide('goog.html.SafeUrl');
        22
        23goog.require('goog.asserts');
        24goog.require('goog.fs.url');
        25goog.require('goog.i18n.bidi.Dir');
        26goog.require('goog.i18n.bidi.DirectionalString');
        27goog.require('goog.string.Const');
        28goog.require('goog.string.TypedString');
        29
        30
        31
        32/**
        33 * A string that is safe to use in URL context in DOM APIs and HTML documents.
        34 *
        35 * A SafeUrl is a string-like object that carries the security type contract
        36 * that its value as a string will not cause untrusted script execution
        37 * when evaluated as a hyperlink URL in a browser.
        38 *
        39 * Values of this type are guaranteed to be safe to use in URL/hyperlink
        40 * contexts, such as, assignment to URL-valued DOM properties, or
        41 * interpolation into a HTML template in URL context (e.g., inside a href
        42 * attribute), in the sense that the use will not result in a
        43 * Cross-Site-Scripting vulnerability.
        44 *
        45 * Note that, as documented in {@code goog.html.SafeUrl.unwrap}, this type's
        46 * contract does not guarantee that instances are safe to interpolate into HTML
        47 * without appropriate escaping.
        48 *
        49 * Note also that this type's contract does not imply any guarantees regarding
        50 * the resource the URL refers to. In particular, SafeUrls are <b>not</b>
        51 * safe to use in a context where the referred-to resource is interpreted as
        52 * trusted code, e.g., as the src of a script tag.
        53 *
        54 * Instances of this type must be created via the factory methods
        55 * ({@code goog.html.SafeUrl.fromConstant}, {@code goog.html.SafeUrl.sanitize}),
        56 * etc and not by invoking its constructor. The constructor intentionally
        57 * takes no parameters and the type is immutable; hence only a default instance
        58 * corresponding to the empty string can be obtained via constructor invocation.
        59 *
        60 * @see goog.html.SafeUrl#fromConstant
        61 * @see goog.html.SafeUrl#from
        62 * @see goog.html.SafeUrl#sanitize
        63 * @constructor
        64 * @final
        65 * @struct
        66 * @implements {goog.i18n.bidi.DirectionalString}
        67 * @implements {goog.string.TypedString}
        68 */
        69goog.html.SafeUrl = function() {
        70 /**
        71 * The contained value of this SafeUrl. The field has a purposely ugly
        72 * name to make (non-compiled) code that attempts to directly access this
        73 * field stand out.
        74 * @private {string}
        75 */
        76 this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
        77
        78 /**
        79 * A type marker used to implement additional run-time type checking.
        80 * @see goog.html.SafeUrl#unwrap
        81 * @const
        82 * @private
        83 */
        84 this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
        85 goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
        86};
        87
        88
        89/**
        90 * The innocuous string generated by goog.html.SafeUrl.sanitize when passed
        91 * an unsafe URL.
        92 *
        93 * about:invalid is registered in
        94 * http://www.w3.org/TR/css3-values/#about-invalid.
        95 * http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to
        96 * contain a fragment, which is not to be considered when determining if an
        97 * about URL is well-known.
        98 *
        99 * Using about:invalid seems preferable to using a fixed data URL, since
        100 * browsers might choose to not report CSP violations on it, as legitimate
        101 * CSS function calls to attr() can result in this URL being produced. It is
        102 * also a standard URL which matches exactly the semantics we need:
        103 * "The about:invalid URI references a non-existent document with a generic
        104 * error condition. It can be used when a URI is necessary, but the default
        105 * value shouldn't be resolveable as any type of document".
        106 *
        107 * @const {string}
        108 */
        109goog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';
        110
        111
        112/**
        113 * @override
        114 * @const
        115 */
        116goog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;
        117
        118
        119/**
        120 * Returns this SafeUrl's value a string.
        121 *
        122 * IMPORTANT: In code where it is security relevant that an object's type is
        123 * indeed {@code SafeUrl}, use {@code goog.html.SafeUrl.unwrap} instead of this
        124 * method. If in doubt, assume that it's security relevant. In particular, note
        125 * that goog.html functions which return a goog.html type do not guarantee that
        126 * the returned instance is of the right type. For example:
        127 *
        128 * <pre>
        129 * var fakeSafeHtml = new String('fake');
        130 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
        131 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
        132 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
        133 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
        134 * // goog.html.SafeHtml.
        135 * </pre>
        136 *
        137 * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
        138 * behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
        139 * be appropriately escaped before embedding in a HTML document. Note that the
        140 * required escaping is context-sensitive (e.g. a different escaping is
        141 * required for embedding a URL in a style property within a style
        142 * attribute, as opposed to embedding in a href attribute).
        143 *
        144 * @see goog.html.SafeUrl#unwrap
        145 * @override
        146 */
        147goog.html.SafeUrl.prototype.getTypedStringValue = function() {
        148 return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
        149};
        150
        151
        152/**
        153 * @override
        154 * @const
        155 */
        156goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString = true;
        157
        158
        159/**
        160 * Returns this URLs directionality, which is always {@code LTR}.
        161 * @override
        162 */
        163goog.html.SafeUrl.prototype.getDirection = function() {
        164 return goog.i18n.bidi.Dir.LTR;
        165};
        166
        167
        168if (goog.DEBUG) {
        169 /**
        170 * Returns a debug string-representation of this value.
        171 *
        172 * To obtain the actual string value wrapped in a SafeUrl, use
        173 * {@code goog.html.SafeUrl.unwrap}.
        174 *
        175 * @see goog.html.SafeUrl#unwrap
        176 * @override
        177 */
        178 goog.html.SafeUrl.prototype.toString = function() {
        179 return 'SafeUrl{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
        180 '}';
        181 };
        182}
        183
        184
        185/**
        186 * Performs a runtime check that the provided object is indeed a SafeUrl
        187 * object, and returns its value.
        188 *
        189 * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
        190 * behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
        191 * be appropriately escaped before embedding in a HTML document. Note that the
        192 * required escaping is context-sensitive (e.g. a different escaping is
        193 * required for embedding a URL in a style property within a style
        194 * attribute, as opposed to embedding in a href attribute).
        195 *
        196 * @param {!goog.html.SafeUrl} safeUrl The object to extract from.
        197 * @return {string} The SafeUrl object's contained string, unless the run-time
        198 * type check fails. In that case, {@code unwrap} returns an innocuous
        199 * string, or, if assertions are enabled, throws
        200 * {@code goog.asserts.AssertionError}.
        201 */
        202goog.html.SafeUrl.unwrap = function(safeUrl) {
        203 // Perform additional Run-time type-checking to ensure that safeUrl is indeed
        204 // an instance of the expected type. This provides some additional protection
        205 // against security bugs due to application code that disables type checks.
        206 // Specifically, the following checks are performed:
        207 // 1. The object is an instance of the expected type.
        208 // 2. The object is not an instance of a subclass.
        209 // 3. The object carries a type marker for the expected type. "Faking" an
        210 // object requires a reference to the type marker, which has names intended
        211 // to stand out in code reviews.
        212 if (safeUrl instanceof goog.html.SafeUrl &&
        213 safeUrl.constructor === goog.html.SafeUrl &&
        214 safeUrl.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
        215 goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
        216 return safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
        217 } else {
        218 goog.asserts.fail('expected object of type SafeUrl, got \'' +
        219 safeUrl + '\'');
        220 return 'type_error:SafeUrl';
        221
        222 }
        223};
        224
        225
        226/**
        227 * Creates a SafeUrl object from a compile-time constant string.
        228 *
        229 * Compile-time constant strings are inherently program-controlled and hence
        230 * trusted.
        231 *
        232 * @param {!goog.string.Const} url A compile-time-constant string from which to
        233 * create a SafeUrl.
        234 * @return {!goog.html.SafeUrl} A SafeUrl object initialized to {@code url}.
        235 */
        236goog.html.SafeUrl.fromConstant = function(url) {
        237 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
        238 goog.string.Const.unwrap(url));
        239};
        240
        241
        242/**
        243 * A pattern that matches Blob or data types that can have SafeUrls created
        244 * from URL.createObjectURL(blob) or via a data: URI. Only matches image and
        245 * video types, currently.
        246 * @const
        247 * @private
        248 */
        249goog.html.SAFE_MIME_TYPE_PATTERN_ =
        250 /^(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm))$/i;
        251
        252
        253/**
        254 * Creates a SafeUrl wrapping a blob URL for the given {@code blob}.
        255 *
        256 * The blob URL is created with {@code URL.createObjectURL}. If the MIME type
        257 * for {@code blob} is not of a known safe image or video MIME type, then the
        258 * SafeUrl will wrap {@link #INNOCUOUS_STRING}.
        259 *
        260 * @see http://www.w3.org/TR/FileAPI/#url
        261 * @param {!Blob} blob
        262 * @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped
        263 * as a SafeUrl.
        264 */
        265goog.html.SafeUrl.fromBlob = function(blob) {
        266 var url = goog.html.SAFE_MIME_TYPE_PATTERN_.test(blob.type) ?
        267 goog.fs.url.createObjectUrl(blob) : goog.html.SafeUrl.INNOCUOUS_STRING;
        268 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
        269};
        270
        271
        272/**
        273 * Matches a base-64 data URL, with the first match group being the MIME type.
        274 * @const
        275 * @private
        276 */
        277goog.html.DATA_URL_PATTERN_ = /^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i;
        278
        279
        280/**
        281 * Creates a SafeUrl wrapping a data: URL, after validating it matches a
        282 * known-safe image or video MIME type.
        283 *
        284 * @param {string} dataUrl A valid base64 data URL with one of the whitelisted
        285 * image or video MIME types.
        286 * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
        287 * wrapped as a SafeUrl if it does not pass.
        288 */
        289goog.html.SafeUrl.fromDataUrl = function(dataUrl) {
        290 // There's a slight risk here that a browser sniffs the content type if it
        291 // doesn't know the MIME type and executes HTML within the data: URL. For this
        292 // to cause XSS it would also have to execute the HTML in the same origin
        293 // of the page with the link. It seems unlikely that both of these will
        294 // happen, particularly in not really old IEs.
        295 var match = dataUrl.match(goog.html.DATA_URL_PATTERN_);
        296 var valid = match && goog.html.SAFE_MIME_TYPE_PATTERN_.test(match[1]);
        297 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
        298 valid ? dataUrl : goog.html.SafeUrl.INNOCUOUS_STRING);
        299};
        300
        301
        302/**
        303 * A pattern that recognizes a commonly useful subset of URLs that satisfy
        304 * the SafeUrl contract.
        305 *
        306 * This regular expression matches a subset of URLs that will not cause script
        307 * execution if used in URL context within a HTML document. Specifically, this
        308 * regular expression matches if (comment from here on and regex copied from
        309 * Soy's EscapingConventions):
        310 * (1) Either a protocol in a whitelist (http, https, mailto or ftp).
        311 * (2) or no protocol. A protocol must be followed by a colon. The below
        312 * allows that by allowing colons only after one of the characters [/?#].
        313 * A colon after a hash (#) must be in the fragment.
        314 * Otherwise, a colon after a (?) must be in a query.
        315 * Otherwise, a colon after a single solidus (/) must be in a path.
        316 * Otherwise, a colon after a double solidus (//) must be in the authority
        317 * (before port).
        318 *
        319 * The pattern disallows &, used in HTML entity declarations before
        320 * one of the characters in [/?#]. This disallows HTML entities used in the
        321 * protocol name, which should never happen, e.g. "h&#116;tp" for "http".
        322 * It also disallows HTML entities in the first path part of a relative path,
        323 * e.g. "foo&lt;bar/baz". Our existing escaping functions should not produce
        324 * that. More importantly, it disallows masking of a colon,
        325 * e.g. "javascript&#58;...".
        326 *
        327 * @private
        328 * @const {!RegExp}
        329 */
        330goog.html.SAFE_URL_PATTERN_ =
        331 /^(?:(?:https?|mailto|ftp):|[^&:/?#]*(?:[/?#]|$))/i;
        332
        333
        334/**
        335 * Creates a SafeUrl object from {@code url}. If {@code url} is a
        336 * goog.html.SafeUrl then it is simply returned. Otherwise the input string is
        337 * validated to match a pattern of commonly used safe URLs. The string is
        338 * converted to UTF-8 and non-whitelisted characters are percent-encoded. The
        339 * string wrapped by the created SafeUrl will thus contain only ASCII printable
        340 * characters.
        341 *
        342 * {@code url} may be a URL with the http, https, mailto or ftp scheme,
        343 * or a relative URL (i.e., a URL without a scheme; specifically, a
        344 * scheme-relative, absolute-path-relative, or path-relative URL).
        345 *
        346 * {@code url} is converted to UTF-8 and non-whitelisted characters are
        347 * percent-encoded. Whitelisted characters are '%' and, from RFC 3986,
        348 * unreserved characters and reserved characters, with the exception of '\'',
        349 * '(' and ')'. This ensures the the SafeUrl contains only ASCII-printable
        350 * characters and reduces the chance of security bugs were it to be
        351 * interpolated into a specific context without the necessary escaping.
        352 *
        353 * If {@code url} fails validation or does not UTF-16 decode correctly
        354 * (JavaScript strings are UTF-16 encoded), this function returns a SafeUrl
        355 * object containing an innocuous string, goog.html.SafeUrl.INNOCUOUS_STRING.
        356 *
        357 * @see http://url.spec.whatwg.org/#concept-relative-url
        358 * @param {string|!goog.string.TypedString} url The URL to validate.
        359 * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
        360 */
        361goog.html.SafeUrl.sanitize = function(url) {
        362 if (url instanceof goog.html.SafeUrl) {
        363 return url;
        364 }
        365 else if (url.implementsGoogStringTypedString) {
        366 url = url.getTypedStringValue();
        367 } else {
        368 url = String(url);
        369 }
        370 if (!goog.html.SAFE_URL_PATTERN_.test(url)) {
        371 url = goog.html.SafeUrl.INNOCUOUS_STRING;
        372 }
        373 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
        374};
        375
        376
        377/**
        378 * Type marker for the SafeUrl type, used to implement additional run-time
        379 * type checking.
        380 * @const {!Object}
        381 * @private
        382 */
        383goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
        384
        385
        386/**
        387 * Package-internal utility method to create SafeUrl instances.
        388 *
        389 * @param {string} url The string to initialize the SafeUrl object with.
        390 * @return {!goog.html.SafeUrl} The initialized SafeUrl object.
        391 * @package
        392 */
        393goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(
        394 url) {
        395 var safeUrl = new goog.html.SafeUrl();
        396 safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = url;
        397 return safeUrl;
        398};
        \ No newline at end of file diff --git a/docs/source/lib/goog/html/trustedresourceurl.js.src.html b/docs/source/lib/goog/html/trustedresourceurl.js.src.html new file mode 100644 index 0000000..1146db6 --- /dev/null +++ b/docs/source/lib/goog/html/trustedresourceurl.js.src.html @@ -0,0 +1 @@ +trustedresourceurl.js

        lib/goog/html/trustedresourceurl.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 The TrustedResourceUrl type and its builders.
        17 *
        18 * TODO(xtof): Link to document stating type contract.
        19 */
        20
        21goog.provide('goog.html.TrustedResourceUrl');
        22
        23goog.require('goog.asserts');
        24goog.require('goog.i18n.bidi.Dir');
        25goog.require('goog.i18n.bidi.DirectionalString');
        26goog.require('goog.string.Const');
        27goog.require('goog.string.TypedString');
        28
        29
        30
        31/**
        32 * A URL which is under application control and from which script, CSS, and
        33 * other resources that represent executable code, can be fetched.
        34 *
        35 * Given that the URL can only be constructed from strings under application
        36 * control and is used to load resources, bugs resulting in a malformed URL
        37 * should not have a security impact and are likely to be easily detectable
        38 * during testing. Given the wide number of non-RFC compliant URLs in use,
        39 * stricter validation could prevent some applications from being able to use
        40 * this type.
        41 *
        42 * Instances of this type must be created via the factory method,
        43 * ({@code goog.html.TrustedResourceUrl.fromConstant}), and not by invoking its
        44 * constructor. The constructor intentionally takes no parameters and the type
        45 * is immutable; hence only a default instance corresponding to the empty
        46 * string can be obtained via constructor invocation.
        47 *
        48 * @see goog.html.TrustedResourceUrl#fromConstant
        49 * @constructor
        50 * @final
        51 * @struct
        52 * @implements {goog.i18n.bidi.DirectionalString}
        53 * @implements {goog.string.TypedString}
        54 */
        55goog.html.TrustedResourceUrl = function() {
        56 /**
        57 * The contained value of this TrustedResourceUrl. The field has a purposely
        58 * ugly name to make (non-compiled) code that attempts to directly access this
        59 * field stand out.
        60 * @private {string}
        61 */
        62 this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ = '';
        63
        64 /**
        65 * A type marker used to implement additional run-time type checking.
        66 * @see goog.html.TrustedResourceUrl#unwrap
        67 * @const
        68 * @private
        69 */
        70 this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
        71 goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
        72};
        73
        74
        75/**
        76 * @override
        77 * @const
        78 */
        79goog.html.TrustedResourceUrl.prototype.implementsGoogStringTypedString = true;
        80
        81
        82/**
        83 * Returns this TrustedResourceUrl's value as a string.
        84 *
        85 * IMPORTANT: In code where it is security relevant that an object's type is
        86 * indeed {@code TrustedResourceUrl}, use
        87 * {@code goog.html.TrustedResourceUrl.unwrap} instead of this method. If in
        88 * doubt, assume that it's security relevant. In particular, note that
        89 * goog.html functions which return a goog.html type do not guarantee that
        90 * the returned instance is of the right type. For example:
        91 *
        92 * <pre>
        93 * var fakeSafeHtml = new String('fake');
        94 * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
        95 * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
        96 * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
        97 * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
        98 * // goog.html.SafeHtml.
        99 * </pre>
        100 *
        101 * @see goog.html.TrustedResourceUrl#unwrap
        102 * @override
        103 */
        104goog.html.TrustedResourceUrl.prototype.getTypedStringValue = function() {
        105 return this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
        106};
        107
        108
        109/**
        110 * @override
        111 * @const
        112 */
        113goog.html.TrustedResourceUrl.prototype.implementsGoogI18nBidiDirectionalString =
        114 true;
        115
        116
        117/**
        118 * Returns this URLs directionality, which is always {@code LTR}.
        119 * @override
        120 */
        121goog.html.TrustedResourceUrl.prototype.getDirection = function() {
        122 return goog.i18n.bidi.Dir.LTR;
        123};
        124
        125
        126if (goog.DEBUG) {
        127 /**
        128 * Returns a debug string-representation of this value.
        129 *
        130 * To obtain the actual string value wrapped in a TrustedResourceUrl, use
        131 * {@code goog.html.TrustedResourceUrl.unwrap}.
        132 *
        133 * @see goog.html.TrustedResourceUrl#unwrap
        134 * @override
        135 */
        136 goog.html.TrustedResourceUrl.prototype.toString = function() {
        137 return 'TrustedResourceUrl{' +
        138 this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ + '}';
        139 };
        140}
        141
        142
        143/**
        144 * Performs a runtime check that the provided object is indeed a
        145 * TrustedResourceUrl object, and returns its value.
        146 *
        147 * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl The object to
        148 * extract from.
        149 * @return {string} The trustedResourceUrl object's contained string, unless
        150 * the run-time type check fails. In that case, {@code unwrap} returns an
        151 * innocuous string, or, if assertions are enabled, throws
        152 * {@code goog.asserts.AssertionError}.
        153 */
        154goog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) {
        155 // Perform additional Run-time type-checking to ensure that
        156 // trustedResourceUrl is indeed an instance of the expected type. This
        157 // provides some additional protection against security bugs due to
        158 // application code that disables type checks.
        159 // Specifically, the following checks are performed:
        160 // 1. The object is an instance of the expected type.
        161 // 2. The object is not an instance of a subclass.
        162 // 3. The object carries a type marker for the expected type. "Faking" an
        163 // object requires a reference to the type marker, which has names intended
        164 // to stand out in code reviews.
        165 if (trustedResourceUrl instanceof goog.html.TrustedResourceUrl &&
        166 trustedResourceUrl.constructor === goog.html.TrustedResourceUrl &&
        167 trustedResourceUrl
        168 .TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
        169 goog.html.TrustedResourceUrl
        170 .TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
        171 return trustedResourceUrl
        172 .privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
        173 } else {
        174 goog.asserts.fail('expected object of type TrustedResourceUrl, got \'' +
        175 trustedResourceUrl + '\'');
        176 return 'type_error:TrustedResourceUrl';
        177
        178 }
        179};
        180
        181
        182/**
        183 * Creates a TrustedResourceUrl object from a compile-time constant string.
        184 *
        185 * Compile-time constant strings are inherently program-controlled and hence
        186 * trusted.
        187 *
        188 * @param {!goog.string.Const} url A compile-time-constant string from which to
        189 * create a TrustedResourceUrl.
        190 * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
        191 * initialized to {@code url}.
        192 */
        193goog.html.TrustedResourceUrl.fromConstant = function(url) {
        194 return goog.html.TrustedResourceUrl
        195 .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(
        196 goog.string.Const.unwrap(url));
        197};
        198
        199
        200/**
        201 * Type marker for the TrustedResourceUrl type, used to implement additional
        202 * run-time type checking.
        203 * @const {!Object}
        204 * @private
        205 */
        206goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
        207
        208
        209/**
        210 * Package-internal utility method to create TrustedResourceUrl instances.
        211 *
        212 * @param {string} url The string to initialize the TrustedResourceUrl object
        213 * with.
        214 * @return {!goog.html.TrustedResourceUrl} The initialized TrustedResourceUrl
        215 * object.
        216 * @package
        217 */
        218goog.html.TrustedResourceUrl.
        219 createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse = function(url) {
        220 var trustedResourceUrl = new goog.html.TrustedResourceUrl();
        221 trustedResourceUrl.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ =
        222 url;
        223 return trustedResourceUrl;
        224};
        \ No newline at end of file diff --git a/docs/source/lib/goog/html/uncheckedconversions.js.src.html b/docs/source/lib/goog/html/uncheckedconversions.js.src.html new file mode 100644 index 0000000..e12549e --- /dev/null +++ b/docs/source/lib/goog/html/uncheckedconversions.js.src.html @@ -0,0 +1 @@ +uncheckedconversions.js

        lib/goog/html/uncheckedconversions.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 Unchecked conversions to create values of goog.html types from
        17 * plain strings. Use of these functions could potentially result in instances
        18 * of goog.html types that violate their type contracts, and hence result in
        19 * security vulnerabilties.
        20 *
        21 * Therefore, all uses of the methods herein must be carefully security
        22 * reviewed. Avoid use of the methods in this file whenever possible; instead
        23 * prefer to create instances of goog.html types using inherently safe builders
        24 * or template systems.
        25 *
        26 *
        27 *
        28 * @visibility {//closure/goog/html:approved_for_unchecked_conversion}
        29 * @visibility {//closure/goog/bin/sizetests:__pkg__}
        30 */
        31
        32
        33goog.provide('goog.html.uncheckedconversions');
        34
        35goog.require('goog.asserts');
        36goog.require('goog.html.SafeHtml');
        37goog.require('goog.html.SafeScript');
        38goog.require('goog.html.SafeStyle');
        39goog.require('goog.html.SafeStyleSheet');
        40goog.require('goog.html.SafeUrl');
        41goog.require('goog.html.TrustedResourceUrl');
        42goog.require('goog.string');
        43goog.require('goog.string.Const');
        44
        45
        46/**
        47 * Performs an "unchecked conversion" to SafeHtml from a plain string that is
        48 * known to satisfy the SafeHtml type contract.
        49 *
        50 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
        51 * that the value of {@code html} satisfies the SafeHtml type contract in all
        52 * possible program states.
        53 *
        54 *
        55 * @param {!goog.string.Const} justification A constant string explaining why
        56 * this use of this method is safe. May include a security review ticket
        57 * number.
        58 * @param {string} html A string that is claimed to adhere to the SafeHtml
        59 * contract.
        60 * @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the
        61 * SafeHtml to be constructed. A null or undefined value signifies an
        62 * unknown directionality.
        63 * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
        64 * object.
        65 * @suppress {visibility} For access to SafeHtml.create... Note that this
        66 * use is appropriate since this method is intended to be "package private"
        67 * withing goog.html. DO NOT call SafeHtml.create... from outside this
        68 * package; use appropriate wrappers instead.
        69 */
        70goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract =
        71 function(justification, html, opt_dir) {
        72 // unwrap() called inside an assert so that justification can be optimized
        73 // away in production code.
        74 goog.asserts.assertString(goog.string.Const.unwrap(justification),
        75 'must provide justification');
        76 goog.asserts.assert(
        77 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
        78 'must provide non-empty justification');
        79 return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
        80 html, opt_dir || null);
        81};
        82
        83
        84/**
        85 * Performs an "unchecked conversion" to SafeScript from a plain string that is
        86 * known to satisfy the SafeScript type contract.
        87 *
        88 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
        89 * that the value of {@code script} satisfies the SafeScript type contract in
        90 * all possible program states.
        91 *
        92 *
        93 * @param {!goog.string.Const} justification A constant string explaining why
        94 * this use of this method is safe. May include a security review ticket
        95 * number.
        96 * @param {string} script The string to wrap as a SafeScript.
        97 * @return {!goog.html.SafeScript} The value of {@code script}, wrapped in a
        98 * SafeScript object.
        99 */
        100goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract =
        101 function(justification, script) {
        102 // unwrap() called inside an assert so that justification can be optimized
        103 // away in production code.
        104 goog.asserts.assertString(goog.string.Const.unwrap(justification),
        105 'must provide justification');
        106 goog.asserts.assert(
        107 !goog.string.isEmpty(goog.string.Const.unwrap(justification)),
        108 'must provide non-empty justification');
        109 return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
        110 script);
        111};
        112
        113
        114/**
        115 * Performs an "unchecked conversion" to SafeStyle from a plain string that is
        116 * known to satisfy the SafeStyle type contract.
        117 *
        118 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
        119 * that the value of {@code style} satisfies the SafeUrl type contract in all
        120 * possible program states.
        121 *
        122 *
        123 * @param {!goog.string.Const} justification A constant string explaining why
        124 * this use of this method is safe. May include a security review ticket
        125 * number.
        126 * @param {string} style The string to wrap as a SafeStyle.
        127 * @return {!goog.html.SafeStyle} The value of {@code style}, wrapped in a
        128 * SafeStyle object.
        129 */
        130goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract =
        131 function(justification, style) {
        132 // unwrap() called inside an assert so that justification can be optimized
        133 // away in production code.
        134 goog.asserts.assertString(goog.string.Const.unwrap(justification),
        135 'must provide justification');
        136 goog.asserts.assert(
        137 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
        138 'must provide non-empty justification');
        139 return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
        140 style);
        141};
        142
        143
        144/**
        145 * Performs an "unchecked conversion" to SafeStyleSheet from a plain string
        146 * that is known to satisfy the SafeStyleSheet type contract.
        147 *
        148 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
        149 * that the value of {@code styleSheet} satisfies the SafeUrl type contract in
        150 * all possible program states.
        151 *
        152 *
        153 * @param {!goog.string.Const} justification A constant string explaining why
        154 * this use of this method is safe. May include a security review ticket
        155 * number.
        156 * @param {string} styleSheet The string to wrap as a SafeStyleSheet.
        157 * @return {!goog.html.SafeStyleSheet} The value of {@code styleSheet}, wrapped
        158 * in a SafeStyleSheet object.
        159 */
        160goog.html.uncheckedconversions.
        161 safeStyleSheetFromStringKnownToSatisfyTypeContract =
        162 function(justification, styleSheet) {
        163 // unwrap() called inside an assert so that justification can be optimized
        164 // away in production code.
        165 goog.asserts.assertString(goog.string.Const.unwrap(justification),
        166 'must provide justification');
        167 goog.asserts.assert(
        168 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
        169 'must provide non-empty justification');
        170 return goog.html.SafeStyleSheet.
        171 createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
        172};
        173
        174
        175/**
        176 * Performs an "unchecked conversion" to SafeUrl from a plain string that is
        177 * known to satisfy the SafeUrl type contract.
        178 *
        179 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
        180 * that the value of {@code url} satisfies the SafeUrl type contract in all
        181 * possible program states.
        182 *
        183 *
        184 * @param {!goog.string.Const} justification A constant string explaining why
        185 * this use of this method is safe. May include a security review ticket
        186 * number.
        187 * @param {string} url The string to wrap as a SafeUrl.
        188 * @return {!goog.html.SafeUrl} The value of {@code url}, wrapped in a SafeUrl
        189 * object.
        190 */
        191goog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract =
        192 function(justification, url) {
        193 // unwrap() called inside an assert so that justification can be optimized
        194 // away in production code.
        195 goog.asserts.assertString(goog.string.Const.unwrap(justification),
        196 'must provide justification');
        197 goog.asserts.assert(
        198 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
        199 'must provide non-empty justification');
        200 return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
        201};
        202
        203
        204/**
        205 * Performs an "unchecked conversion" to TrustedResourceUrl from a plain string
        206 * that is known to satisfy the TrustedResourceUrl type contract.
        207 *
        208 * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
        209 * that the value of {@code url} satisfies the TrustedResourceUrl type contract
        210 * in all possible program states.
        211 *
        212 *
        213 * @param {!goog.string.Const} justification A constant string explaining why
        214 * this use of this method is safe. May include a security review ticket
        215 * number.
        216 * @param {string} url The string to wrap as a TrustedResourceUrl.
        217 * @return {!goog.html.TrustedResourceUrl} The value of {@code url}, wrapped in
        218 * a TrustedResourceUrl object.
        219 */
        220goog.html.uncheckedconversions.
        221 trustedResourceUrlFromStringKnownToSatisfyTypeContract =
        222 function(justification, url) {
        223 // unwrap() called inside an assert so that justification can be optimized
        224 // away in production code.
        225 goog.asserts.assertString(goog.string.Const.unwrap(justification),
        226 'must provide justification');
        227 goog.asserts.assert(
        228 !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
        229 'must provide non-empty justification');
        230 return goog.html.TrustedResourceUrl.
        231 createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
        232};
        \ No newline at end of file diff --git a/docs/source/lib/goog/i18n/bidi.js.src.html b/docs/source/lib/goog/i18n/bidi.js.src.html new file mode 100644 index 0000000..34b4bf6 --- /dev/null +++ b/docs/source/lib/goog/i18n/bidi.js.src.html @@ -0,0 +1 @@ +bidi.js

        lib/goog/i18n/bidi.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 Utility functions for supporting Bidi issues.
        17 */
        18
        19
        20/**
        21 * Namespace for bidi supporting functions.
        22 */
        23goog.provide('goog.i18n.bidi');
        24goog.provide('goog.i18n.bidi.Dir');
        25goog.provide('goog.i18n.bidi.DirectionalString');
        26goog.provide('goog.i18n.bidi.Format');
        27
        28
        29/**
        30 * @define {boolean} FORCE_RTL forces the {@link goog.i18n.bidi.IS_RTL} constant
        31 * to say that the current locale is a RTL locale. This should only be used
        32 * if you want to override the default behavior for deciding whether the
        33 * current locale is RTL or not.
        34 *
        35 * {@see goog.i18n.bidi.IS_RTL}
        36 */
        37goog.define('goog.i18n.bidi.FORCE_RTL', false);
        38
        39
        40/**
        41 * Constant that defines whether or not the current locale is a RTL locale.
        42 * If {@link goog.i18n.bidi.FORCE_RTL} is not true, this constant will default
        43 * to check that {@link goog.LOCALE} is one of a few major RTL locales.
        44 *
        45 * <p>This is designed to be a maximally efficient compile-time constant. For
        46 * example, for the default goog.LOCALE, compiling
        47 * "if (goog.i18n.bidi.IS_RTL) alert('rtl') else {}" should produce no code. It
        48 * is this design consideration that limits the implementation to only
        49 * supporting a few major RTL locales, as opposed to the broader repertoire of
        50 * something like goog.i18n.bidi.isRtlLanguage.
        51 *
        52 * <p>Since this constant refers to the directionality of the locale, it is up
        53 * to the caller to determine if this constant should also be used for the
        54 * direction of the UI.
        55 *
        56 * {@see goog.LOCALE}
        57 *
        58 * @type {boolean}
        59 *
        60 * TODO(user): write a test that checks that this is a compile-time constant.
        61 */
        62goog.i18n.bidi.IS_RTL = goog.i18n.bidi.FORCE_RTL ||
        63 (
        64 (goog.LOCALE.substring(0, 2).toLowerCase() == 'ar' ||
        65 goog.LOCALE.substring(0, 2).toLowerCase() == 'fa' ||
        66 goog.LOCALE.substring(0, 2).toLowerCase() == 'he' ||
        67 goog.LOCALE.substring(0, 2).toLowerCase() == 'iw' ||
        68 goog.LOCALE.substring(0, 2).toLowerCase() == 'ps' ||
        69 goog.LOCALE.substring(0, 2).toLowerCase() == 'sd' ||
        70 goog.LOCALE.substring(0, 2).toLowerCase() == 'ug' ||
        71 goog.LOCALE.substring(0, 2).toLowerCase() == 'ur' ||
        72 goog.LOCALE.substring(0, 2).toLowerCase() == 'yi') &&
        73 (goog.LOCALE.length == 2 ||
        74 goog.LOCALE.substring(2, 3) == '-' ||
        75 goog.LOCALE.substring(2, 3) == '_')
        76 ) || (
        77 goog.LOCALE.length >= 3 &&
        78 goog.LOCALE.substring(0, 3).toLowerCase() == 'ckb' &&
        79 (goog.LOCALE.length == 3 ||
        80 goog.LOCALE.substring(3, 4) == '-' ||
        81 goog.LOCALE.substring(3, 4) == '_')
        82 );
        83
        84
        85/**
        86 * Unicode formatting characters and directionality string constants.
        87 * @enum {string}
        88 */
        89goog.i18n.bidi.Format = {
        90 /** Unicode "Left-To-Right Embedding" (LRE) character. */
        91 LRE: '\u202A',
        92 /** Unicode "Right-To-Left Embedding" (RLE) character. */
        93 RLE: '\u202B',
        94 /** Unicode "Pop Directional Formatting" (PDF) character. */
        95 PDF: '\u202C',
        96 /** Unicode "Left-To-Right Mark" (LRM) character. */
        97 LRM: '\u200E',
        98 /** Unicode "Right-To-Left Mark" (RLM) character. */
        99 RLM: '\u200F'
        100};
        101
        102
        103/**
        104 * Directionality enum.
        105 * @enum {number}
        106 */
        107goog.i18n.bidi.Dir = {
        108 /**
        109 * Left-to-right.
        110 */
        111 LTR: 1,
        112
        113 /**
        114 * Right-to-left.
        115 */
        116 RTL: -1,
        117
        118 /**
        119 * Neither left-to-right nor right-to-left.
        120 */
        121 NEUTRAL: 0
        122};
        123
        124
        125/**
        126 * 'right' string constant.
        127 * @type {string}
        128 */
        129goog.i18n.bidi.RIGHT = 'right';
        130
        131
        132/**
        133 * 'left' string constant.
        134 * @type {string}
        135 */
        136goog.i18n.bidi.LEFT = 'left';
        137
        138
        139/**
        140 * 'left' if locale is RTL, 'right' if not.
        141 * @type {string}
        142 */
        143goog.i18n.bidi.I18N_RIGHT = goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.LEFT :
        144 goog.i18n.bidi.RIGHT;
        145
        146
        147/**
        148 * 'right' if locale is RTL, 'left' if not.
        149 * @type {string}
        150 */
        151goog.i18n.bidi.I18N_LEFT = goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.RIGHT :
        152 goog.i18n.bidi.LEFT;
        153
        154
        155/**
        156 * Convert a directionality given in various formats to a goog.i18n.bidi.Dir
        157 * constant. Useful for interaction with different standards of directionality
        158 * representation.
        159 *
        160 * @param {goog.i18n.bidi.Dir|number|boolean|null} givenDir Directionality given
        161 * in one of the following formats:
        162 * 1. A goog.i18n.bidi.Dir constant.
        163 * 2. A number (positive = LTR, negative = RTL, 0 = neutral).
        164 * 3. A boolean (true = RTL, false = LTR).
        165 * 4. A null for unknown directionality.
        166 * @param {boolean=} opt_noNeutral Whether a givenDir of zero or
        167 * goog.i18n.bidi.Dir.NEUTRAL should be treated as null, i.e. unknown, in
        168 * order to preserve legacy behavior.
        169 * @return {?goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the
        170 * given directionality. If given null, returns null (i.e. unknown).
        171 */
        172goog.i18n.bidi.toDir = function(givenDir, opt_noNeutral) {
        173 if (typeof givenDir == 'number') {
        174 // This includes the non-null goog.i18n.bidi.Dir case.
        175 return givenDir > 0 ? goog.i18n.bidi.Dir.LTR :
        176 givenDir < 0 ? goog.i18n.bidi.Dir.RTL :
        177 opt_noNeutral ? null : goog.i18n.bidi.Dir.NEUTRAL;
        178 } else if (givenDir == null) {
        179 return null;
        180 } else {
        181 // Must be typeof givenDir == 'boolean'.
        182 return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
        183 }
        184};
        185
        186
        187/**
        188 * A practical pattern to identify strong LTR characters. This pattern is not
        189 * theoretically correct according to the Unicode standard. It is simplified for
        190 * performance and small code size.
        191 * @type {string}
        192 * @private
        193 */
        194goog.i18n.bidi.ltrChars_ =
        195 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' +
        196 '\u200E\u2C00-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF';
        197
        198
        199/**
        200 * A practical pattern to identify strong RTL character. This pattern is not
        201 * theoretically correct according to the Unicode standard. It is simplified
        202 * for performance and small code size.
        203 * @type {string}
        204 * @private
        205 */
        206goog.i18n.bidi.rtlChars_ =
        207 '\u0591-\u06EF\u06FA-\u07FF\u200F\uFB1D-\uFDFF\uFE70-\uFEFC';
        208
        209
        210/**
        211 * Simplified regular expression for an HTML tag (opening or closing) or an HTML
        212 * escape. We might want to skip over such expressions when estimating the text
        213 * directionality.
        214 * @type {RegExp}
        215 * @private
        216 */
        217goog.i18n.bidi.htmlSkipReg_ = /<[^>]*>|&[^;]+;/g;
        218
        219
        220/**
        221 * Returns the input text with spaces instead of HTML tags or HTML escapes, if
        222 * opt_isStripNeeded is true. Else returns the input as is.
        223 * Useful for text directionality estimation.
        224 * Note: the function should not be used in other contexts; it is not 100%
        225 * correct, but rather a good-enough implementation for directionality
        226 * estimation purposes.
        227 * @param {string} str The given string.
        228 * @param {boolean=} opt_isStripNeeded Whether to perform the stripping.
        229 * Default: false (to retain consistency with calling functions).
        230 * @return {string} The given string cleaned of HTML tags / escapes.
        231 * @private
        232 */
        233goog.i18n.bidi.stripHtmlIfNeeded_ = function(str, opt_isStripNeeded) {
        234 return opt_isStripNeeded ? str.replace(goog.i18n.bidi.htmlSkipReg_, '') :
        235 str;
        236};
        237
        238
        239/**
        240 * Regular expression to check for RTL characters.
        241 * @type {RegExp}
        242 * @private
        243 */
        244goog.i18n.bidi.rtlCharReg_ = new RegExp('[' + goog.i18n.bidi.rtlChars_ + ']');
        245
        246
        247/**
        248 * Regular expression to check for LTR characters.
        249 * @type {RegExp}
        250 * @private
        251 */
        252goog.i18n.bidi.ltrCharReg_ = new RegExp('[' + goog.i18n.bidi.ltrChars_ + ']');
        253
        254
        255/**
        256 * Test whether the given string has any RTL characters in it.
        257 * @param {string} str The given string that need to be tested.
        258 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        259 * Default: false.
        260 * @return {boolean} Whether the string contains RTL characters.
        261 */
        262goog.i18n.bidi.hasAnyRtl = function(str, opt_isHtml) {
        263 return goog.i18n.bidi.rtlCharReg_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
        264 str, opt_isHtml));
        265};
        266
        267
        268/**
        269 * Test whether the given string has any RTL characters in it.
        270 * @param {string} str The given string that need to be tested.
        271 * @return {boolean} Whether the string contains RTL characters.
        272 * @deprecated Use hasAnyRtl.
        273 */
        274goog.i18n.bidi.hasRtlChar = goog.i18n.bidi.hasAnyRtl;
        275
        276
        277/**
        278 * Test whether the given string has any LTR characters in it.
        279 * @param {string} str The given string that need to be tested.
        280 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        281 * Default: false.
        282 * @return {boolean} Whether the string contains LTR characters.
        283 */
        284goog.i18n.bidi.hasAnyLtr = function(str, opt_isHtml) {
        285 return goog.i18n.bidi.ltrCharReg_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
        286 str, opt_isHtml));
        287};
        288
        289
        290/**
        291 * Regular expression pattern to check if the first character in the string
        292 * is LTR.
        293 * @type {RegExp}
        294 * @private
        295 */
        296goog.i18n.bidi.ltrRe_ = new RegExp('^[' + goog.i18n.bidi.ltrChars_ + ']');
        297
        298
        299/**
        300 * Regular expression pattern to check if the first character in the string
        301 * is RTL.
        302 * @type {RegExp}
        303 * @private
        304 */
        305goog.i18n.bidi.rtlRe_ = new RegExp('^[' + goog.i18n.bidi.rtlChars_ + ']');
        306
        307
        308/**
        309 * Check if the first character in the string is RTL or not.
        310 * @param {string} str The given string that need to be tested.
        311 * @return {boolean} Whether the first character in str is an RTL char.
        312 */
        313goog.i18n.bidi.isRtlChar = function(str) {
        314 return goog.i18n.bidi.rtlRe_.test(str);
        315};
        316
        317
        318/**
        319 * Check if the first character in the string is LTR or not.
        320 * @param {string} str The given string that need to be tested.
        321 * @return {boolean} Whether the first character in str is an LTR char.
        322 */
        323goog.i18n.bidi.isLtrChar = function(str) {
        324 return goog.i18n.bidi.ltrRe_.test(str);
        325};
        326
        327
        328/**
        329 * Check if the first character in the string is neutral or not.
        330 * @param {string} str The given string that need to be tested.
        331 * @return {boolean} Whether the first character in str is a neutral char.
        332 */
        333goog.i18n.bidi.isNeutralChar = function(str) {
        334 return !goog.i18n.bidi.isLtrChar(str) && !goog.i18n.bidi.isRtlChar(str);
        335};
        336
        337
        338/**
        339 * Regular expressions to check if a piece of text is of LTR directionality
        340 * on first character with strong directionality.
        341 * @type {RegExp}
        342 * @private
        343 */
        344goog.i18n.bidi.ltrDirCheckRe_ = new RegExp(
        345 '^[^' + goog.i18n.bidi.rtlChars_ + ']*[' + goog.i18n.bidi.ltrChars_ + ']');
        346
        347
        348/**
        349 * Regular expressions to check if a piece of text is of RTL directionality
        350 * on first character with strong directionality.
        351 * @type {RegExp}
        352 * @private
        353 */
        354goog.i18n.bidi.rtlDirCheckRe_ = new RegExp(
        355 '^[^' + goog.i18n.bidi.ltrChars_ + ']*[' + goog.i18n.bidi.rtlChars_ + ']');
        356
        357
        358/**
        359 * Check whether the first strongly directional character (if any) is RTL.
        360 * @param {string} str String being checked.
        361 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        362 * Default: false.
        363 * @return {boolean} Whether RTL directionality is detected using the first
        364 * strongly-directional character method.
        365 */
        366goog.i18n.bidi.startsWithRtl = function(str, opt_isHtml) {
        367 return goog.i18n.bidi.rtlDirCheckRe_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
        368 str, opt_isHtml));
        369};
        370
        371
        372/**
        373 * Check whether the first strongly directional character (if any) is RTL.
        374 * @param {string} str String being checked.
        375 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        376 * Default: false.
        377 * @return {boolean} Whether RTL directionality is detected using the first
        378 * strongly-directional character method.
        379 * @deprecated Use startsWithRtl.
        380 */
        381goog.i18n.bidi.isRtlText = goog.i18n.bidi.startsWithRtl;
        382
        383
        384/**
        385 * Check whether the first strongly directional character (if any) is LTR.
        386 * @param {string} str String being checked.
        387 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        388 * Default: false.
        389 * @return {boolean} Whether LTR directionality is detected using the first
        390 * strongly-directional character method.
        391 */
        392goog.i18n.bidi.startsWithLtr = function(str, opt_isHtml) {
        393 return goog.i18n.bidi.ltrDirCheckRe_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
        394 str, opt_isHtml));
        395};
        396
        397
        398/**
        399 * Check whether the first strongly directional character (if any) is LTR.
        400 * @param {string} str String being checked.
        401 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        402 * Default: false.
        403 * @return {boolean} Whether LTR directionality is detected using the first
        404 * strongly-directional character method.
        405 * @deprecated Use startsWithLtr.
        406 */
        407goog.i18n.bidi.isLtrText = goog.i18n.bidi.startsWithLtr;
        408
        409
        410/**
        411 * Regular expression to check if a string looks like something that must
        412 * always be LTR even in RTL text, e.g. a URL. When estimating the
        413 * directionality of text containing these, we treat these as weakly LTR,
        414 * like numbers.
        415 * @type {RegExp}
        416 * @private
        417 */
        418goog.i18n.bidi.isRequiredLtrRe_ = /^http:\/\/.*/;
        419
        420
        421/**
        422 * Check whether the input string either contains no strongly directional
        423 * characters or looks like a url.
        424 * @param {string} str String being checked.
        425 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        426 * Default: false.
        427 * @return {boolean} Whether neutral directionality is detected.
        428 */
        429goog.i18n.bidi.isNeutralText = function(str, opt_isHtml) {
        430 str = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml);
        431 return goog.i18n.bidi.isRequiredLtrRe_.test(str) ||
        432 !goog.i18n.bidi.hasAnyLtr(str) && !goog.i18n.bidi.hasAnyRtl(str);
        433};
        434
        435
        436/**
        437 * Regular expressions to check if the last strongly-directional character in a
        438 * piece of text is LTR.
        439 * @type {RegExp}
        440 * @private
        441 */
        442goog.i18n.bidi.ltrExitDirCheckRe_ = new RegExp(
        443 '[' + goog.i18n.bidi.ltrChars_ + '][^' + goog.i18n.bidi.rtlChars_ + ']*$');
        444
        445
        446/**
        447 * Regular expressions to check if the last strongly-directional character in a
        448 * piece of text is RTL.
        449 * @type {RegExp}
        450 * @private
        451 */
        452goog.i18n.bidi.rtlExitDirCheckRe_ = new RegExp(
        453 '[' + goog.i18n.bidi.rtlChars_ + '][^' + goog.i18n.bidi.ltrChars_ + ']*$');
        454
        455
        456/**
        457 * Check if the exit directionality a piece of text is LTR, i.e. if the last
        458 * strongly-directional character in the string is LTR.
        459 * @param {string} str String being checked.
        460 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        461 * Default: false.
        462 * @return {boolean} Whether LTR exit directionality was detected.
        463 */
        464goog.i18n.bidi.endsWithLtr = function(str, opt_isHtml) {
        465 return goog.i18n.bidi.ltrExitDirCheckRe_.test(
        466 goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
        467};
        468
        469
        470/**
        471 * Check if the exit directionality a piece of text is LTR, i.e. if the last
        472 * strongly-directional character in the string is LTR.
        473 * @param {string} str String being checked.
        474 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        475 * Default: false.
        476 * @return {boolean} Whether LTR exit directionality was detected.
        477 * @deprecated Use endsWithLtr.
        478 */
        479goog.i18n.bidi.isLtrExitText = goog.i18n.bidi.endsWithLtr;
        480
        481
        482/**
        483 * Check if the exit directionality a piece of text is RTL, i.e. if the last
        484 * strongly-directional character in the string is RTL.
        485 * @param {string} str String being checked.
        486 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        487 * Default: false.
        488 * @return {boolean} Whether RTL exit directionality was detected.
        489 */
        490goog.i18n.bidi.endsWithRtl = function(str, opt_isHtml) {
        491 return goog.i18n.bidi.rtlExitDirCheckRe_.test(
        492 goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
        493};
        494
        495
        496/**
        497 * Check if the exit directionality a piece of text is RTL, i.e. if the last
        498 * strongly-directional character in the string is RTL.
        499 * @param {string} str String being checked.
        500 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        501 * Default: false.
        502 * @return {boolean} Whether RTL exit directionality was detected.
        503 * @deprecated Use endsWithRtl.
        504 */
        505goog.i18n.bidi.isRtlExitText = goog.i18n.bidi.endsWithRtl;
        506
        507
        508/**
        509 * A regular expression for matching right-to-left language codes.
        510 * See {@link #isRtlLanguage} for the design.
        511 * @type {RegExp}
        512 * @private
        513 */
        514goog.i18n.bidi.rtlLocalesRe_ = new RegExp(
        515 '^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|' +
        516 '.*[-_](Arab|Hebr|Thaa|Nkoo|Tfng))' +
        517 '(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)',
        518 'i');
        519
        520
        521/**
        522 * Check if a BCP 47 / III language code indicates an RTL language, i.e. either:
        523 * - a language code explicitly specifying one of the right-to-left scripts,
        524 * e.g. "az-Arab", or<p>
        525 * - a language code specifying one of the languages normally written in a
        526 * right-to-left script, e.g. "fa" (Farsi), except ones explicitly specifying
        527 * Latin or Cyrillic script (which are the usual LTR alternatives).<p>
        528 * The list of right-to-left scripts appears in the 100-199 range in
        529 * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
        530 * Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
        531 * Tifinagh, which also have significant modern usage. The rest (Syriac,
        532 * Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
        533 * and are not recognized to save on code size.
        534 * The languages usually written in a right-to-left script are taken as those
        535 * with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng in
        536 * http://www.iana.org/assignments/language-subtag-registry,
        537 * as well as Central (or Sorani) Kurdish (ckb), Sindhi (sd) and Uyghur (ug).
        538 * Other subtags of the language code, e.g. regions like EG (Egypt), are
        539 * ignored.
        540 * @param {string} lang BCP 47 (a.k.a III) language code.
        541 * @return {boolean} Whether the language code is an RTL language.
        542 */
        543goog.i18n.bidi.isRtlLanguage = function(lang) {
        544 return goog.i18n.bidi.rtlLocalesRe_.test(lang);
        545};
        546
        547
        548/**
        549 * Regular expression for bracket guard replacement in html.
        550 * @type {RegExp}
        551 * @private
        552 */
        553goog.i18n.bidi.bracketGuardHtmlRe_ =
        554 /(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(&lt;.*?(&gt;)+)/g;
        555
        556
        557/**
        558 * Regular expression for bracket guard replacement in text.
        559 * @type {RegExp}
        560 * @private
        561 */
        562goog.i18n.bidi.bracketGuardTextRe_ =
        563 /(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)/g;
        564
        565
        566/**
        567 * Apply bracket guard using html span tag. This is to address the problem of
        568 * messy bracket display frequently happens in RTL layout.
        569 * @param {string} s The string that need to be processed.
        570 * @param {boolean=} opt_isRtlContext specifies default direction (usually
        571 * direction of the UI).
        572 * @return {string} The processed string, with all bracket guarded.
        573 */
        574goog.i18n.bidi.guardBracketInHtml = function(s, opt_isRtlContext) {
        575 var useRtl = opt_isRtlContext === undefined ?
        576 goog.i18n.bidi.hasAnyRtl(s) : opt_isRtlContext;
        577 if (useRtl) {
        578 return s.replace(goog.i18n.bidi.bracketGuardHtmlRe_,
        579 '<span dir=rtl>$&</span>');
        580 }
        581 return s.replace(goog.i18n.bidi.bracketGuardHtmlRe_,
        582 '<span dir=ltr>$&</span>');
        583};
        584
        585
        586/**
        587 * Apply bracket guard using LRM and RLM. This is to address the problem of
        588 * messy bracket display frequently happens in RTL layout.
        589 * This version works for both plain text and html. But it does not work as
        590 * good as guardBracketInHtml in some cases.
        591 * @param {string} s The string that need to be processed.
        592 * @param {boolean=} opt_isRtlContext specifies default direction (usually
        593 * direction of the UI).
        594 * @return {string} The processed string, with all bracket guarded.
        595 */
        596goog.i18n.bidi.guardBracketInText = function(s, opt_isRtlContext) {
        597 var useRtl = opt_isRtlContext === undefined ?
        598 goog.i18n.bidi.hasAnyRtl(s) : opt_isRtlContext;
        599 var mark = useRtl ? goog.i18n.bidi.Format.RLM : goog.i18n.bidi.Format.LRM;
        600 return s.replace(goog.i18n.bidi.bracketGuardTextRe_, mark + '$&' + mark);
        601};
        602
        603
        604/**
        605 * Enforce the html snippet in RTL directionality regardless overall context.
        606 * If the html piece was enclosed by tag, dir will be applied to existing
        607 * tag, otherwise a span tag will be added as wrapper. For this reason, if
        608 * html snippet start with with tag, this tag must enclose the whole piece. If
        609 * the tag already has a dir specified, this new one will override existing
        610 * one in behavior (tested on FF and IE).
        611 * @param {string} html The string that need to be processed.
        612 * @return {string} The processed string, with directionality enforced to RTL.
        613 */
        614goog.i18n.bidi.enforceRtlInHtml = function(html) {
        615 if (html.charAt(0) == '<') {
        616 return html.replace(/<\w+/, '$& dir=rtl');
        617 }
        618 // '\n' is important for FF so that it won't incorrectly merge span groups
        619 return '\n<span dir=rtl>' + html + '</span>';
        620};
        621
        622
        623/**
        624 * Enforce RTL on both end of the given text piece using unicode BiDi formatting
        625 * characters RLE and PDF.
        626 * @param {string} text The piece of text that need to be wrapped.
        627 * @return {string} The wrapped string after process.
        628 */
        629goog.i18n.bidi.enforceRtlInText = function(text) {
        630 return goog.i18n.bidi.Format.RLE + text + goog.i18n.bidi.Format.PDF;
        631};
        632
        633
        634/**
        635 * Enforce the html snippet in RTL directionality regardless overall context.
        636 * If the html piece was enclosed by tag, dir will be applied to existing
        637 * tag, otherwise a span tag will be added as wrapper. For this reason, if
        638 * html snippet start with with tag, this tag must enclose the whole piece. If
        639 * the tag already has a dir specified, this new one will override existing
        640 * one in behavior (tested on FF and IE).
        641 * @param {string} html The string that need to be processed.
        642 * @return {string} The processed string, with directionality enforced to RTL.
        643 */
        644goog.i18n.bidi.enforceLtrInHtml = function(html) {
        645 if (html.charAt(0) == '<') {
        646 return html.replace(/<\w+/, '$& dir=ltr');
        647 }
        648 // '\n' is important for FF so that it won't incorrectly merge span groups
        649 return '\n<span dir=ltr>' + html + '</span>';
        650};
        651
        652
        653/**
        654 * Enforce LTR on both end of the given text piece using unicode BiDi formatting
        655 * characters LRE and PDF.
        656 * @param {string} text The piece of text that need to be wrapped.
        657 * @return {string} The wrapped string after process.
        658 */
        659goog.i18n.bidi.enforceLtrInText = function(text) {
        660 return goog.i18n.bidi.Format.LRE + text + goog.i18n.bidi.Format.PDF;
        661};
        662
        663
        664/**
        665 * Regular expression to find dimensions such as "padding: .3 0.4ex 5px 6;"
        666 * @type {RegExp}
        667 * @private
        668 */
        669goog.i18n.bidi.dimensionsRe_ =
        670 /:\s*([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)/g;
        671
        672
        673/**
        674 * Regular expression for left.
        675 * @type {RegExp}
        676 * @private
        677 */
        678goog.i18n.bidi.leftRe_ = /left/gi;
        679
        680
        681/**
        682 * Regular expression for right.
        683 * @type {RegExp}
        684 * @private
        685 */
        686goog.i18n.bidi.rightRe_ = /right/gi;
        687
        688
        689/**
        690 * Placeholder regular expression for swapping.
        691 * @type {RegExp}
        692 * @private
        693 */
        694goog.i18n.bidi.tempRe_ = /%%%%/g;
        695
        696
        697/**
        698 * Swap location parameters and 'left'/'right' in CSS specification. The
        699 * processed string will be suited for RTL layout. Though this function can
        700 * cover most cases, there are always exceptions. It is suggested to put
        701 * those exceptions in separate group of CSS string.
        702 * @param {string} cssStr CSS spefication string.
        703 * @return {string} Processed CSS specification string.
        704 */
        705goog.i18n.bidi.mirrorCSS = function(cssStr) {
        706 return cssStr.
        707 // reverse dimensions
        708 replace(goog.i18n.bidi.dimensionsRe_, ':$1 $4 $3 $2').
        709 replace(goog.i18n.bidi.leftRe_, '%%%%'). // swap left and right
        710 replace(goog.i18n.bidi.rightRe_, goog.i18n.bidi.LEFT).
        711 replace(goog.i18n.bidi.tempRe_, goog.i18n.bidi.RIGHT);
        712};
        713
        714
        715/**
        716 * Regular expression for hebrew double quote substitution, finding quote
        717 * directly after hebrew characters.
        718 * @type {RegExp}
        719 * @private
        720 */
        721goog.i18n.bidi.doubleQuoteSubstituteRe_ = /([\u0591-\u05f2])"/g;
        722
        723
        724/**
        725 * Regular expression for hebrew single quote substitution, finding quote
        726 * directly after hebrew characters.
        727 * @type {RegExp}
        728 * @private
        729 */
        730goog.i18n.bidi.singleQuoteSubstituteRe_ = /([\u0591-\u05f2])'/g;
        731
        732
        733/**
        734 * Replace the double and single quote directly after a Hebrew character with
        735 * GERESH and GERSHAYIM. In such case, most likely that's user intention.
        736 * @param {string} str String that need to be processed.
        737 * @return {string} Processed string with double/single quote replaced.
        738 */
        739goog.i18n.bidi.normalizeHebrewQuote = function(str) {
        740 return str.
        741 replace(goog.i18n.bidi.doubleQuoteSubstituteRe_, '$1\u05f4').
        742 replace(goog.i18n.bidi.singleQuoteSubstituteRe_, '$1\u05f3');
        743};
        744
        745
        746/**
        747 * Regular expression to split a string into "words" for directionality
        748 * estimation based on relative word counts.
        749 * @type {RegExp}
        750 * @private
        751 */
        752goog.i18n.bidi.wordSeparatorRe_ = /\s+/;
        753
        754
        755/**
        756 * Regular expression to check if a string contains any numerals. Used to
        757 * differentiate between completely neutral strings and those containing
        758 * numbers, which are weakly LTR.
        759 *
        760 * Native Arabic digits (\u0660 - \u0669) are not included because although they
        761 * do flow left-to-right inside a number, this is the case even if the overall
        762 * directionality is RTL, and a mathematical expression using these digits is
        763 * supposed to flow right-to-left overall, including unary plus and minus
        764 * appearing to the right of a number, and this does depend on the overall
        765 * directionality being RTL. The digits used in Farsi (\u06F0 - \u06F9), on the
        766 * other hand, are included, since Farsi math (including unary plus and minus)
        767 * does flow left-to-right.
        768 *
        769 * @type {RegExp}
        770 * @private
        771 */
        772goog.i18n.bidi.hasNumeralsRe_ = /[\d\u06f0-\u06f9]/;
        773
        774
        775/**
        776 * This constant controls threshold of RTL directionality.
        777 * @type {number}
        778 * @private
        779 */
        780goog.i18n.bidi.rtlDetectionThreshold_ = 0.40;
        781
        782
        783/**
        784 * Estimates the directionality of a string based on relative word counts.
        785 * If the number of RTL words is above a certain percentage of the total number
        786 * of strongly directional words, returns RTL.
        787 * Otherwise, if any words are strongly or weakly LTR, returns LTR.
        788 * Otherwise, returns UNKNOWN, which is used to mean "neutral".
        789 * Numbers are counted as weakly LTR.
        790 * @param {string} str The string to be checked.
        791 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        792 * Default: false.
        793 * @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
        794 */
        795goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) {
        796 var rtlCount = 0;
        797 var totalCount = 0;
        798 var hasWeaklyLtr = false;
        799 var tokens = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml).
        800 split(goog.i18n.bidi.wordSeparatorRe_);
        801 for (var i = 0; i < tokens.length; i++) {
        802 var token = tokens[i];
        803 if (goog.i18n.bidi.startsWithRtl(token)) {
        804 rtlCount++;
        805 totalCount++;
        806 } else if (goog.i18n.bidi.isRequiredLtrRe_.test(token)) {
        807 hasWeaklyLtr = true;
        808 } else if (goog.i18n.bidi.hasAnyLtr(token)) {
        809 totalCount++;
        810 } else if (goog.i18n.bidi.hasNumeralsRe_.test(token)) {
        811 hasWeaklyLtr = true;
        812 }
        813 }
        814
        815 return totalCount == 0 ?
        816 (hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.NEUTRAL) :
        817 (rtlCount / totalCount > goog.i18n.bidi.rtlDetectionThreshold_ ?
        818 goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR);
        819};
        820
        821
        822/**
        823 * Check the directionality of a piece of text, return true if the piece of
        824 * text should be laid out in RTL direction.
        825 * @param {string} str The piece of text that need to be detected.
        826 * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
        827 * Default: false.
        828 * @return {boolean} Whether this piece of text should be laid out in RTL.
        829 */
        830goog.i18n.bidi.detectRtlDirectionality = function(str, opt_isHtml) {
        831 return goog.i18n.bidi.estimateDirection(str, opt_isHtml) ==
        832 goog.i18n.bidi.Dir.RTL;
        833};
        834
        835
        836/**
        837 * Sets text input element's directionality and text alignment based on a
        838 * given directionality. Does nothing if the given directionality is unknown or
        839 * neutral.
        840 * @param {Element} element Input field element to set directionality to.
        841 * @param {goog.i18n.bidi.Dir|number|boolean|null} dir Desired directionality,
        842 * given in one of the following formats:
        843 * 1. A goog.i18n.bidi.Dir constant.
        844 * 2. A number (positive = LRT, negative = RTL, 0 = neutral).
        845 * 3. A boolean (true = RTL, false = LTR).
        846 * 4. A null for unknown directionality.
        847 */
        848goog.i18n.bidi.setElementDirAndAlign = function(element, dir) {
        849 if (element) {
        850 dir = goog.i18n.bidi.toDir(dir);
        851 if (dir) {
        852 element.style.textAlign =
        853 dir == goog.i18n.bidi.Dir.RTL ?
        854 goog.i18n.bidi.RIGHT : goog.i18n.bidi.LEFT;
        855 element.dir = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
        856 }
        857 }
        858};
        859
        860
        861/**
        862 * Sets element dir based on estimated directionality of the given text.
        863 * @param {!Element} element
        864 * @param {string} text
        865 */
        866goog.i18n.bidi.setElementDirByTextDirectionality = function(element, text) {
        867 switch (goog.i18n.bidi.estimateDirection(text)) {
        868 case (goog.i18n.bidi.Dir.LTR):
        869 element.dir = 'ltr';
        870 break;
        871 case (goog.i18n.bidi.Dir.RTL):
        872 element.dir = 'rtl';
        873 break;
        874 default:
        875 // Default for no direction, inherit from document.
        876 element.removeAttribute('dir');
        877 }
        878};
        879
        880
        881
        882/**
        883 * Strings that have an (optional) known direction.
        884 *
        885 * Implementations of this interface are string-like objects that carry an
        886 * attached direction, if known.
        887 * @interface
        888 */
        889goog.i18n.bidi.DirectionalString = function() {};
        890
        891
        892/**
        893 * Interface marker of the DirectionalString interface.
        894 *
        895 * This property can be used to determine at runtime whether or not an object
        896 * implements this interface. All implementations of this interface set this
        897 * property to {@code true}.
        898 * @type {boolean}
        899 */
        900goog.i18n.bidi.DirectionalString.prototype.
        901 implementsGoogI18nBidiDirectionalString;
        902
        903
        904/**
        905 * Retrieves this object's known direction (if any).
        906 * @return {?goog.i18n.bidi.Dir} The known direction. Null if unknown.
        907 */
        908goog.i18n.bidi.DirectionalString.prototype.getDirection;
        \ No newline at end of file diff --git a/docs/source/lib/goog/iter/iter.js.src.html b/docs/source/lib/goog/iter/iter.js.src.html index d75b9d2..88959c2 100644 --- a/docs/source/lib/goog/iter/iter.js.src.html +++ b/docs/source/lib/goog/iter/iter.js.src.html @@ -1 +1 @@ -iter.js

        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
        21goog.provide('goog.iter');
        22goog.provide('goog.iter.Iterator');
        23goog.provide('goog.iter.StopIteration');
        24
        25goog.require('goog.array');
        26goog.require('goog.asserts');
        27
        28
        29// TODO(nnaze): Add more functions from Python's itertools.
        30// http://docs.python.org/library/itertools.html
        31
        32
        33/**
        34 * @typedef {goog.iter.Iterator|{length:number}|{__iterator__}}
        35 */
        36goog.iter.Iterable;
        37
        38
        39// For script engines that already support iterators.
        40if ('StopIteration' in goog.global) {
        41 /**
        42 * Singleton Error object that is used to terminate iterations.
        43 * @type {Error}
        44 */
        45 goog.iter.StopIteration = goog.global['StopIteration'];
        46} else {
        47 /**
        48 * Singleton Error object that is used to terminate iterations.
        49 * @type {Error}
        50 * @suppress {duplicate}
        51 */
        52 goog.iter.StopIteration = Error('StopIteration');
        53}
        54
        55
        56
        57/**
        58 * Class/interface for iterators. An iterator needs to implement a {@code next}
        59 * method and it needs to throw a {@code goog.iter.StopIteration} when the
        60 * iteration passes beyond the end. Iterators have no {@code hasNext} method.
        61 * It is recommended to always use the helper functions to iterate over the
        62 * iterator or in case you are only targeting JavaScript 1.7 for in loops.
        63 * @constructor
        64 */
        65goog.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 {*} Any object or value.
        72 */
        73goog.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} The object itself.
        87 */
        88goog.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.Iterable} iterable If the object is an iterator it
        96 * will be returned as is. If the object has a {@code __iterator__} method
        97 * that will be called to get the value iterator. If the object is an
        98 * array-like object we create an iterator for that.
        99 * @return {!goog.iter.Iterator} An iterator that knows how to iterate over the
        100 * values in {@code iterable}.
        101 */
        102goog.iter.toIterator = function(iterable) {
        103 if (iterable instanceof goog.iter.Iterator) {
        104 return iterable;
        105 }
        106 if (typeof iterable.__iterator__ == 'function') {
        107 return iterable.__iterator__(false);
        108 }
        109 if (goog.isArrayLike(iterable)) {
        110 var i = 0;
        111 var newIter = new goog.iter.Iterator;
        112 newIter.next = function() {
        113 while (true) {
        114 if (i >= iterable.length) {
        115 throw goog.iter.StopIteration;
        116 }
        117 // Don't include deleted elements.
        118 if (!(i in iterable)) {
        119 i++;
        120 continue;
        121 }
        122 return iterable[i++];
        123 }
        124 };
        125 return newIter;
        126 }
        127
        128
        129 // TODO(arv): Should we fall back on goog.structs.getValues()?
        130 throw Error('Not implemented');
        131};
        132
        133
        134/**
        135 * Calls a function for each element in the iterator with the element of the
        136 * iterator passed as argument.
        137 *
        138 * @param {goog.iter.Iterable} iterable The iterator to iterate
        139 * over. If the iterable is an object {@code toIterator} will be called on
        140 * it.
        141* @param {function(this:T,?,?,?):?} f The function to call for every
        142 * element. This function
        143 * takes 3 arguments (the element, undefined, and the iterator) and the
        144 * return value is irrelevant. The reason for passing undefined as the
        145 * second argument is so that the same function can be used in
        146 * {@see goog.array#forEach} as well as others.
        147 * @param {T=} opt_obj The object to be used as the value of 'this' within
        148 * {@code f}.
        149 * @template T
        150 */
        151goog.iter.forEach = function(iterable, f, opt_obj) {
        152 if (goog.isArrayLike(iterable)) {
        153 /** @preserveTry */
        154 try {
        155 // NOTES: this passes the index number to the second parameter
        156 // of the callback contrary to the documentation above.
        157 goog.array.forEach(/** @type {goog.array.ArrayLike} */(iterable), f,
        158 opt_obj);
        159 } catch (ex) {
        160 if (ex !== goog.iter.StopIteration) {
        161 throw ex;
        162 }
        163 }
        164 } else {
        165 iterable = goog.iter.toIterator(iterable);
        166 /** @preserveTry */
        167 try {
        168 while (true) {
        169 f.call(opt_obj, iterable.next(), undefined, iterable);
        170 }
        171 } catch (ex) {
        172 if (ex !== goog.iter.StopIteration) {
        173 throw ex;
        174 }
        175 }
        176 }
        177};
        178
        179
        180/**
        181 * Calls a function for every element in the iterator, and if the function
        182 * returns true adds the element to a new iterator.
        183 *
        184 * @param {goog.iter.Iterable} iterable The iterator to iterate over.
        185 * @param {function(this:T,?,undefined,?):boolean} f The function to call for
        186 * every element. This function
        187 * takes 3 arguments (the element, undefined, and the iterator) and should
        188 * return a boolean. If the return value is true the element will be
        189 * included in the returned iteror. If it is false the element is not
        190 * included.
        191 * @param {T=} opt_obj The object to be used as the value of 'this' within
        192 * {@code f}.
        193 * @return {!goog.iter.Iterator} A new iterator in which only elements that
        194 * passed the test are present.
        195 * @template T
        196 */
        197goog.iter.filter = function(iterable, f, opt_obj) {
        198 var iterator = goog.iter.toIterator(iterable);
        199 var newIter = new goog.iter.Iterator;
        200 newIter.next = function() {
        201 while (true) {
        202 var val = iterator.next();
        203 if (f.call(opt_obj, val, undefined, iterator)) {
        204 return val;
        205 }
        206 }
        207 };
        208 return newIter;
        209};
        210
        211
        212/**
        213 * Creates a new iterator that returns the values in a range. This function
        214 * can take 1, 2 or 3 arguments:
        215 * <pre>
        216 * range(5) same as range(0, 5, 1)
        217 * range(2, 5) same as range(2, 5, 1)
        218 * </pre>
        219 *
        220 * @param {number} startOrStop The stop value if only one argument is provided.
        221 * The start value if 2 or more arguments are provided. If only one
        222 * argument is used the start value is 0.
        223 * @param {number=} opt_stop The stop value. If left out then the first
        224 * argument is used as the stop value.
        225 * @param {number=} opt_step The number to increment with between each call to
        226 * next. This can be negative.
        227 * @return {!goog.iter.Iterator} A new iterator that returns the values in the
        228 * range.
        229 */
        230goog.iter.range = function(startOrStop, opt_stop, opt_step) {
        231 var start = 0;
        232 var stop = startOrStop;
        233 var step = opt_step || 1;
        234 if (arguments.length > 1) {
        235 start = startOrStop;
        236 stop = opt_stop;
        237 }
        238 if (step == 0) {
        239 throw Error('Range step argument must not be zero');
        240 }
        241
        242 var newIter = new goog.iter.Iterator;
        243 newIter.next = function() {
        244 if (step > 0 && start >= stop || step < 0 && start <= stop) {
        245 throw goog.iter.StopIteration;
        246 }
        247 var rv = start;
        248 start += step;
        249 return rv;
        250 };
        251 return newIter;
        252};
        253
        254
        255/**
        256 * Joins the values in a iterator with a delimiter.
        257 * @param {goog.iter.Iterable} iterable The iterator to get the values from.
        258 * @param {string} deliminator The text to put between the values.
        259 * @return {string} The joined value string.
        260 */
        261goog.iter.join = function(iterable, deliminator) {
        262 return goog.iter.toArray(iterable).join(deliminator);
        263};
        264
        265
        266/**
        267 * For every element in the iterator call a function and return a new iterator
        268 * with that value.
        269 *
        270 * @param {goog.iter.Iterable} iterable The iterator to iterate over.
        271 * @param {function(this:T,?,undefined,?):?} f The function to call for every
        272 * element. This function
        273 * takes 3 arguments (the element, undefined, and the iterator) and should
        274 * return a new value.
        275 * @param {T=} opt_obj The object to be used as the value of 'this' within
        276 * {@code f}.
        277 * @return {!goog.iter.Iterator} A new iterator that returns the results of
        278 * applying the function to each element in the original iterator.
        279 * @template T
        280 */
        281goog.iter.map = function(iterable, f, opt_obj) {
        282 var iterator = goog.iter.toIterator(iterable);
        283 var newIter = new goog.iter.Iterator;
        284 newIter.next = function() {
        285 while (true) {
        286 var val = iterator.next();
        287 return f.call(opt_obj, val, undefined, iterator);
        288 }
        289 };
        290 return newIter;
        291};
        292
        293
        294/**
        295 * Passes every element of an iterator into a function and accumulates the
        296 * result.
        297 *
        298 * @param {goog.iter.Iterable} iterable The iterator to iterate over.
        299 * @param {function(this:T,V,?):V} f The function to call for every
        300 * element. This function takes 2 arguments (the function's previous result
        301 * or the initial value, and the value of the current element).
        302 * function(previousValue, currentElement) : newValue.
        303 * @param {V} val The initial value to pass into the function on the first call.
        304 * @param {T=} opt_obj The object to be used as the value of 'this'
        305 * within f.
        306 * @return {V} Result of evaluating f repeatedly across the values of
        307 * the iterator.
        308 * @template T,V
        309 */
        310goog.iter.reduce = function(iterable, f, val, opt_obj) {
        311 var rval = val;
        312 goog.iter.forEach(iterable, function(val) {
        313 rval = f.call(opt_obj, rval, val);
        314 });
        315 return rval;
        316};
        317
        318
        319/**
        320 * Goes through the values in the iterator. Calls f for each these and if any of
        321 * them returns true, this returns true (without checking the rest). If all
        322 * return false this will return false.
        323 *
        324 * @param {goog.iter.Iterable} iterable The iterator object.
        325 * @param {function(this:T,?,undefined,?):boolean} f The function to call for
        326 * every value. This function
        327 * takes 3 arguments (the value, undefined, and the iterator) and should
        328 * return a boolean.
        329 * @param {T=} opt_obj The object to be used as the value of 'this' within
        330 * {@code f}.
        331 * @return {boolean} true if any value passes the test.
        332 * @template T
        333 */
        334goog.iter.some = function(iterable, f, opt_obj) {
        335 iterable = goog.iter.toIterator(iterable);
        336 /** @preserveTry */
        337 try {
        338 while (true) {
        339 if (f.call(opt_obj, iterable.next(), undefined, iterable)) {
        340 return true;
        341 }
        342 }
        343 } catch (ex) {
        344 if (ex !== goog.iter.StopIteration) {
        345 throw ex;
        346 }
        347 }
        348 return false;
        349};
        350
        351
        352/**
        353 * Goes through the values in the iterator. Calls f for each these and if any of
        354 * them returns false this returns false (without checking the rest). If all
        355 * return true this will return true.
        356 *
        357 * @param {goog.iter.Iterable} iterable The iterator object.
        358 * @param {function(this:T,?,undefined,?):boolean} f The function to call for
        359 * every value. This function
        360 * takes 3 arguments (the value, undefined, and the iterator) and should
        361 * return a boolean.
        362 * @param {T=} opt_obj The object to be used as the value of 'this' within
        363 * {@code f}.
        364 * @return {boolean} true if every value passes the test.
        365 * @template T
        366 */
        367goog.iter.every = function(iterable, f, opt_obj) {
        368 iterable = goog.iter.toIterator(iterable);
        369 /** @preserveTry */
        370 try {
        371 while (true) {
        372 if (!f.call(opt_obj, iterable.next(), undefined, iterable)) {
        373 return false;
        374 }
        375 }
        376 } catch (ex) {
        377 if (ex !== goog.iter.StopIteration) {
        378 throw ex;
        379 }
        380 }
        381 return true;
        382};
        383
        384
        385/**
        386 * Takes zero or more iterators and returns one iterator that will iterate over
        387 * them in the order chained.
        388 * @param {...goog.iter.Iterator} var_args Any number of iterator objects.
        389 * @return {!goog.iter.Iterator} Returns a new iterator that will iterate over
        390 * all the given iterators' contents.
        391 */
        392goog.iter.chain = function(var_args) {
        393 var args = arguments;
        394 var length = args.length;
        395 var i = 0;
        396 var newIter = new goog.iter.Iterator;
        397
        398 /**
        399 * @return {*} The next item in the iteration.
        400 * @this {goog.iter.Iterator}
        401 */
        402 newIter.next = function() {
        403 /** @preserveTry */
        404 try {
        405 if (i >= length) {
        406 throw goog.iter.StopIteration;
        407 }
        408 var current = goog.iter.toIterator(args[i]);
        409 return current.next();
        410 } catch (ex) {
        411 if (ex !== goog.iter.StopIteration || i >= length) {
        412 throw ex;
        413 } else {
        414 // In case we got a StopIteration increment counter and try again.
        415 i++;
        416 return this.next();
        417 }
        418 }
        419 };
        420
        421 return newIter;
        422};
        423
        424
        425/**
        426 * Builds a new iterator that iterates over the original, but skips elements as
        427 * long as a supplied function returns true.
        428 * @param {goog.iter.Iterable} iterable The iterator object.
        429 * @param {function(this:T,?,undefined,?):boolean} f The function to call for
        430 * every value. This function
        431 * takes 3 arguments (the value, undefined, and the iterator) and should
        432 * return a boolean.
        433 * @param {T=} opt_obj The object to be used as the value of 'this' within
        434 * {@code f}.
        435 * @return {!goog.iter.Iterator} A new iterator that drops elements from the
        436 * original iterator as long as {@code f} is true.
        437 * @template T
        438 */
        439goog.iter.dropWhile = function(iterable, f, opt_obj) {
        440 var iterator = goog.iter.toIterator(iterable);
        441 var newIter = new goog.iter.Iterator;
        442 var dropping = true;
        443 newIter.next = function() {
        444 while (true) {
        445 var val = iterator.next();
        446 if (dropping && f.call(opt_obj, val, undefined, iterator)) {
        447 continue;
        448 } else {
        449 dropping = false;
        450 }
        451 return val;
        452 }
        453 };
        454 return newIter;
        455};
        456
        457
        458/**
        459 * Builds a new iterator that iterates over the original, but only as long as a
        460 * supplied function returns true.
        461 * @param {goog.iter.Iterable} iterable The iterator object.
        462 * @param {function(this:T,?,undefined,?):boolean} f The function to call for
        463 * every value. This function
        464 * takes 3 arguments (the value, undefined, and the iterator) and should
        465 * return a boolean.
        466 * @param {T=} opt_obj This is used as the 'this' object in f when called.
        467 * @return {!goog.iter.Iterator} A new iterator that keeps elements in the
        468 * original iterator as long as the function is true.
        469 * @template T
        470 */
        471goog.iter.takeWhile = function(iterable, f, opt_obj) {
        472 var iterator = goog.iter.toIterator(iterable);
        473 var newIter = new goog.iter.Iterator;
        474 var taking = true;
        475 newIter.next = function() {
        476 while (true) {
        477 if (taking) {
        478 var val = iterator.next();
        479 if (f.call(opt_obj, val, undefined, iterator)) {
        480 return val;
        481 } else {
        482 taking = false;
        483 }
        484 } else {
        485 throw goog.iter.StopIteration;
        486 }
        487 }
        488 };
        489 return newIter;
        490};
        491
        492
        493/**
        494 * Converts the iterator to an array
        495 * @param {goog.iter.Iterable} iterable The iterator to convert to an array.
        496 * @return {!Array} An array of the elements the iterator iterates over.
        497 */
        498goog.iter.toArray = function(iterable) {
        499 // Fast path for array-like.
        500 if (goog.isArrayLike(iterable)) {
        501 return goog.array.toArray(/** @type {!goog.array.ArrayLike} */(iterable));
        502 }
        503 iterable = goog.iter.toIterator(iterable);
        504 var array = [];
        505 goog.iter.forEach(iterable, function(val) {
        506 array.push(val);
        507 });
        508 return array;
        509};
        510
        511
        512/**
        513 * Iterates over 2 iterators and returns true if they contain the same sequence
        514 * of elements and have the same length.
        515 * @param {goog.iter.Iterable} iterable1 The first iterable object.
        516 * @param {goog.iter.Iterable} iterable2 The second iterable object.
        517 * @return {boolean} true if the iterators contain the same sequence of
        518 * elements and have the same length.
        519 */
        520goog.iter.equals = function(iterable1, iterable2) {
        521 iterable1 = goog.iter.toIterator(iterable1);
        522 iterable2 = goog.iter.toIterator(iterable2);
        523 var b1, b2;
        524 /** @preserveTry */
        525 try {
        526 while (true) {
        527 b1 = b2 = false;
        528 var val1 = iterable1.next();
        529 b1 = true;
        530 var val2 = iterable2.next();
        531 b2 = true;
        532 if (val1 != val2) {
        533 return false;
        534 }
        535 }
        536 } catch (ex) {
        537 if (ex !== goog.iter.StopIteration) {
        538 throw ex;
        539 } else {
        540 if (b1 && !b2) {
        541 // iterable1 done but iterable2 is not done.
        542 return false;
        543 }
        544 if (!b2) {
        545 /** @preserveTry */
        546 try {
        547 // iterable2 not done?
        548 val2 = iterable2.next();
        549 // iterable2 not done but iterable1 is done
        550 return false;
        551 } catch (ex1) {
        552 if (ex1 !== goog.iter.StopIteration) {
        553 throw ex1;
        554 }
        555 // iterable2 done as well... They are equal
        556 return true;
        557 }
        558 }
        559 }
        560 }
        561 return false;
        562};
        563
        564
        565/**
        566 * Advances the iterator to the next position, returning the given default value
        567 * instead of throwing an exception if the iterator has no more entries.
        568 * @param {goog.iter.Iterable} iterable The iterable object.
        569 * @param {*} defaultValue The value to return if the iterator is empty.
        570 * @return {*} The next item in the iteration, or defaultValue if the iterator
        571 * was empty.
        572 */
        573goog.iter.nextOrValue = function(iterable, defaultValue) {
        574 try {
        575 return goog.iter.toIterator(iterable).next();
        576 } catch (e) {
        577 if (e != goog.iter.StopIteration) {
        578 throw e;
        579 }
        580 return defaultValue;
        581 }
        582};
        583
        584
        585/**
        586 * Cartesian product of zero or more sets. Gives an iterator that gives every
        587 * combination of one element chosen from each set. For example,
        588 * ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).
        589 * @see http://docs.python.org/library/itertools.html#itertools.product
        590 * @param {...!goog.array.ArrayLike.<*>} var_args Zero or more sets, as arrays.
        591 * @return {!goog.iter.Iterator} An iterator that gives each n-tuple (as an
        592 * array).
        593 */
        594goog.iter.product = function(var_args) {
        595 var someArrayEmpty = goog.array.some(arguments, function(arr) {
        596 return !arr.length;
        597 });
        598
        599 // An empty set in a cartesian product gives an empty set.
        600 if (someArrayEmpty || !arguments.length) {
        601 return new goog.iter.Iterator();
        602 }
        603
        604 var iter = new goog.iter.Iterator();
        605 var arrays = arguments;
        606
        607 // The first indicies are [0, 0, ...]
        608 var indicies = goog.array.repeat(0, arrays.length);
        609
        610 iter.next = function() {
        611
        612 if (indicies) {
        613 var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) {
        614 return arrays[arrayIndex][valueIndex];
        615 });
        616
        617 // Generate the next-largest indicies for the next call.
        618 // Increase the rightmost index. If it goes over, increase the next
        619 // rightmost (like carry-over addition).
        620 for (var i = indicies.length - 1; i >= 0; i--) {
        621 // Assertion prevents compiler warning below.
        622 goog.asserts.assert(indicies);
        623 if (indicies[i] < arrays[i].length - 1) {
        624 indicies[i]++;
        625 break;
        626 }
        627
        628 // We're at the last indicies (the last element of every array), so
        629 // the iteration is over on the next call.
        630 if (i == 0) {
        631 indicies = null;
        632 break;
        633 }
        634 // Reset the index in this column and loop back to increment the
        635 // next one.
        636 indicies[i] = 0;
        637 }
        638 return retVal;
        639 }
        640
        641 throw goog.iter.StopIteration;
        642 };
        643
        644 return iter;
        645};
        646
        647
        648/**
        649 * Create an iterator to cycle over the iterable's elements indefinitely.
        650 * For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...
        651 * @see: http://docs.python.org/library/itertools.html#itertools.cycle.
        652 * @param {!goog.iter.Iterable} iterable The iterable object.
        653 * @return {!goog.iter.Iterator} An iterator that iterates indefinitely over
        654 * the values in {@code iterable}.
        655 */
        656goog.iter.cycle = function(iterable) {
        657
        658 var baseIterator = goog.iter.toIterator(iterable);
        659
        660 // We maintain a cache to store the iterable elements as we iterate
        661 // over them. The cache is used to return elements once we have
        662 // iterated over the iterable once.
        663 var cache = [];
        664 var cacheIndex = 0;
        665
        666 var iter = new goog.iter.Iterator();
        667
        668 // This flag is set after the iterable is iterated over once
        669 var useCache = false;
        670
        671 iter.next = function() {
        672 var returnElement = null;
        673
        674 // Pull elements off the original iterator if not using cache
        675 if (!useCache) {
        676 try {
        677 // Return the element from the iterable
        678 returnElement = baseIterator.next();
        679 cache.push(returnElement);
        680 return returnElement;
        681 } catch (e) {
        682 // If an exception other than StopIteration is thrown
        683 // or if there are no elements to iterate over (the iterable was empty)
        684 // throw an exception
        685 if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) {
        686 throw e;
        687 }
        688 // set useCache to true after we know that a 'StopIteration' exception
        689 // was thrown and the cache is not empty (to handle the 'empty iterable'
        690 // use case)
        691 useCache = true;
        692 }
        693 }
        694
        695 returnElement = cache[cacheIndex];
        696 cacheIndex = (cacheIndex + 1) % cache.length;
        697
        698 return returnElement;
        699 };
        700
        701 return iter;
        702};
        \ No newline at end of file +iter.js

        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
        21goog.provide('goog.iter');
        22goog.provide('goog.iter.Iterable');
        23goog.provide('goog.iter.Iterator');
        24goog.provide('goog.iter.StopIteration');
        25
        26goog.require('goog.array');
        27goog.require('goog.asserts');
        28goog.require('goog.functions');
        29goog.require('goog.math');
        30
        31
        32/**
        33 * @typedef {goog.iter.Iterator|{length:number}|{__iterator__}}
        34 */
        35goog.iter.Iterable;
        36
        37
        38/**
        39 * Singleton Error object that is used to terminate iterations.
        40 * @const {!Error}
        41 */
        42goog.iter.StopIteration = ('StopIteration' in goog.global) ?
        43 // For script engines that support legacy iterators.
        44 goog.global['StopIteration'] :
        45 { message: 'StopIteration', stack: ''};
        46
        47
        48
        49/**
        50 * Class/interface for iterators. An iterator needs to implement a {@code next}
        51 * method and it needs to throw a {@code goog.iter.StopIteration} when the
        52 * iteration passes beyond the end. Iterators have no {@code hasNext} method.
        53 * It is recommended to always use the helper functions to iterate over the
        54 * iterator or in case you are only targeting JavaScript 1.7 for in loops.
        55 * @constructor
        56 * @template VALUE
        57 */
        58goog.iter.Iterator = function() {};
        59
        60
        61/**
        62 * Returns the next value of the iteration. This will throw the object
        63 * {@see goog.iter#StopIteration} when the iteration passes the end.
        64 * @return {VALUE} Any object or value.
        65 */
        66goog.iter.Iterator.prototype.next = function() {
        67 throw goog.iter.StopIteration;
        68};
        69
        70
        71/**
        72 * Returns the {@code Iterator} object itself. This is used to implement
        73 * the iterator protocol in JavaScript 1.7
        74 * @param {boolean=} opt_keys Whether to return the keys or values. Default is
        75 * to only return the values. This is being used by the for-in loop (true)
        76 * and the for-each-in loop (false). Even though the param gives a hint
        77 * about what the iterator will return there is no guarantee that it will
        78 * return the keys when true is passed.
        79 * @return {!goog.iter.Iterator<VALUE>} The object itself.
        80 */
        81goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) {
        82 return this;
        83};
        84
        85
        86/**
        87 * Returns an iterator that knows how to iterate over the values in the object.
        88 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable If the
        89 * object is an iterator it will be returned as is. If the object has an
        90 * {@code __iterator__} method that will be called to get the value
        91 * iterator. If the object is an array-like object we create an iterator
        92 * for that.
        93 * @return {!goog.iter.Iterator<VALUE>} An iterator that knows how to iterate
        94 * over the values in {@code iterable}.
        95 * @template VALUE
        96 */
        97goog.iter.toIterator = function(iterable) {
        98 if (iterable instanceof goog.iter.Iterator) {
        99 return iterable;
        100 }
        101 if (typeof iterable.__iterator__ == 'function') {
        102 return iterable.__iterator__(false);
        103 }
        104 if (goog.isArrayLike(iterable)) {
        105 var i = 0;
        106 var newIter = new goog.iter.Iterator;
        107 newIter.next = function() {
        108 while (true) {
        109 if (i >= iterable.length) {
        110 throw goog.iter.StopIteration;
        111 }
        112 // Don't include deleted elements.
        113 if (!(i in iterable)) {
        114 i++;
        115 continue;
        116 }
        117 return iterable[i++];
        118 }
        119 };
        120 return newIter;
        121 }
        122
        123
        124 // TODO(arv): Should we fall back on goog.structs.getValues()?
        125 throw Error('Not implemented');
        126};
        127
        128
        129/**
        130 * Calls a function for each element in the iterator with the element of the
        131 * iterator passed as argument.
        132 *
        133 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        134 * to iterate over. If the iterable is an object {@code toIterator} will be
        135 * called on it.
        136 * @param {function(this:THIS,VALUE,?,!goog.iter.Iterator<VALUE>)} f
        137 * The function to call for every element. This function takes 3 arguments
        138 * (the element, undefined, and the iterator) and the return value is
        139 * irrelevant. The reason for passing undefined as the second argument is
        140 * so that the same function can be used in {@see goog.array#forEach} as
        141 * well as others. The third parameter is of type "number" for
        142 * arraylike objects, undefined, otherwise.
        143 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        144 * {@code f}.
        145 * @template THIS, VALUE
        146 */
        147goog.iter.forEach = function(iterable, f, opt_obj) {
        148 if (goog.isArrayLike(iterable)) {
        149 /** @preserveTry */
        150 try {
        151 // NOTES: this passes the index number to the second parameter
        152 // of the callback contrary to the documentation above.
        153 goog.array.forEach(/** @type {goog.array.ArrayLike} */(iterable), f,
        154 opt_obj);
        155 } catch (ex) {
        156 if (ex !== goog.iter.StopIteration) {
        157 throw ex;
        158 }
        159 }
        160 } else {
        161 iterable = goog.iter.toIterator(iterable);
        162 /** @preserveTry */
        163 try {
        164 while (true) {
        165 f.call(opt_obj, iterable.next(), undefined, iterable);
        166 }
        167 } catch (ex) {
        168 if (ex !== goog.iter.StopIteration) {
        169 throw ex;
        170 }
        171 }
        172 }
        173};
        174
        175
        176/**
        177 * Calls a function for every element in the iterator, and if the function
        178 * returns true adds the element to a new iterator.
        179 *
        180 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        181 * to iterate over.
        182 * @param {
        183 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
        184 * The function to call for every element. This function takes 3 arguments
        185 * (the element, undefined, and the iterator) and should return a boolean.
        186 * If the return value is true the element will be included in the returned
        187 * iterator. If it is false the element is not included.
        188 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        189 * {@code f}.
        190 * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
        191 * that passed the test are present.
        192 * @template THIS, VALUE
        193 */
        194goog.iter.filter = function(iterable, f, opt_obj) {
        195 var iterator = goog.iter.toIterator(iterable);
        196 var newIter = new goog.iter.Iterator;
        197 newIter.next = function() {
        198 while (true) {
        199 var val = iterator.next();
        200 if (f.call(opt_obj, val, undefined, iterator)) {
        201 return val;
        202 }
        203 }
        204 };
        205 return newIter;
        206};
        207
        208
        209/**
        210 * Calls a function for every element in the iterator, and if the function
        211 * returns false adds the element to a new iterator.
        212 *
        213 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        214 * to iterate over.
        215 * @param {
        216 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
        217 * The function to call for every element. This function takes 3 arguments
        218 * (the element, undefined, and the iterator) and should return a boolean.
        219 * If the return value is false the element will be included in the returned
        220 * iterator. If it is true the element is not included.
        221 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        222 * {@code f}.
        223 * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
        224 * that did not pass the test are present.
        225 * @template THIS, VALUE
        226 */
        227goog.iter.filterFalse = function(iterable, f, opt_obj) {
        228 return goog.iter.filter(iterable, goog.functions.not(f), opt_obj);
        229};
        230
        231
        232/**
        233 * Creates a new iterator that returns the values in a range. This function
        234 * can take 1, 2 or 3 arguments:
        235 * <pre>
        236 * range(5) same as range(0, 5, 1)
        237 * range(2, 5) same as range(2, 5, 1)
        238 * </pre>
        239 *
        240 * @param {number} startOrStop The stop value if only one argument is provided.
        241 * The start value if 2 or more arguments are provided. If only one
        242 * argument is used the start value is 0.
        243 * @param {number=} opt_stop The stop value. If left out then the first
        244 * argument is used as the stop value.
        245 * @param {number=} opt_step The number to increment with between each call to
        246 * next. This can be negative.
        247 * @return {!goog.iter.Iterator<number>} A new iterator that returns the values
        248 * in the range.
        249 */
        250goog.iter.range = function(startOrStop, opt_stop, opt_step) {
        251 var start = 0;
        252 var stop = startOrStop;
        253 var step = opt_step || 1;
        254 if (arguments.length > 1) {
        255 start = startOrStop;
        256 stop = opt_stop;
        257 }
        258 if (step == 0) {
        259 throw Error('Range step argument must not be zero');
        260 }
        261
        262 var newIter = new goog.iter.Iterator;
        263 newIter.next = function() {
        264 if (step > 0 && start >= stop || step < 0 && start <= stop) {
        265 throw goog.iter.StopIteration;
        266 }
        267 var rv = start;
        268 start += step;
        269 return rv;
        270 };
        271 return newIter;
        272};
        273
        274
        275/**
        276 * Joins the values in a iterator with a delimiter.
        277 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        278 * to get the values from.
        279 * @param {string} deliminator The text to put between the values.
        280 * @return {string} The joined value string.
        281 * @template VALUE
        282 */
        283goog.iter.join = function(iterable, deliminator) {
        284 return goog.iter.toArray(iterable).join(deliminator);
        285};
        286
        287
        288/**
        289 * For every element in the iterator call a function and return a new iterator
        290 * with that value.
        291 *
        292 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        293 * iterator to iterate over.
        294 * @param {
        295 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):RESULT} f
        296 * The function to call for every element. This function takes 3 arguments
        297 * (the element, undefined, and the iterator) and should return a new value.
        298 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        299 * {@code f}.
        300 * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
        301 * results of applying the function to each element in the original
        302 * iterator.
        303 * @template THIS, VALUE, RESULT
        304 */
        305goog.iter.map = function(iterable, f, opt_obj) {
        306 var iterator = goog.iter.toIterator(iterable);
        307 var newIter = new goog.iter.Iterator;
        308 newIter.next = function() {
        309 var val = iterator.next();
        310 return f.call(opt_obj, val, undefined, iterator);
        311 };
        312 return newIter;
        313};
        314
        315
        316/**
        317 * Passes every element of an iterator into a function and accumulates the
        318 * result.
        319 *
        320 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        321 * to iterate over.
        322 * @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for
        323 * every element. This function takes 2 arguments (the function's previous
        324 * result or the initial value, and the value of the current element).
        325 * function(previousValue, currentElement) : newValue.
        326 * @param {VALUE} val The initial value to pass into the function on the first
        327 * call.
        328 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        329 * f.
        330 * @return {VALUE} Result of evaluating f repeatedly across the values of
        331 * the iterator.
        332 * @template THIS, VALUE
        333 */
        334goog.iter.reduce = function(iterable, f, val, opt_obj) {
        335 var rval = val;
        336 goog.iter.forEach(iterable, function(val) {
        337 rval = f.call(opt_obj, rval, val);
        338 });
        339 return rval;
        340};
        341
        342
        343/**
        344 * Goes through the values in the iterator. Calls f for each of these, and if
        345 * any of them returns true, this returns true (without checking the rest). If
        346 * all return false this will return false.
        347 *
        348 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        349 * object.
        350 * @param {
        351 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
        352 * The function to call for every value. This function takes 3 arguments
        353 * (the value, undefined, and the iterator) and should return a boolean.
        354 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        355 * {@code f}.
        356 * @return {boolean} true if any value passes the test.
        357 * @template THIS, VALUE
        358 */
        359goog.iter.some = function(iterable, f, opt_obj) {
        360 iterable = goog.iter.toIterator(iterable);
        361 /** @preserveTry */
        362 try {
        363 while (true) {
        364 if (f.call(opt_obj, iterable.next(), undefined, iterable)) {
        365 return true;
        366 }
        367 }
        368 } catch (ex) {
        369 if (ex !== goog.iter.StopIteration) {
        370 throw ex;
        371 }
        372 }
        373 return false;
        374};
        375
        376
        377/**
        378 * Goes through the values in the iterator. Calls f for each of these and if any
        379 * of them returns false this returns false (without checking the rest). If all
        380 * return true this will return true.
        381 *
        382 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        383 * object.
        384 * @param {
        385 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
        386 * The function to call for every value. This function takes 3 arguments
        387 * (the value, undefined, and the iterator) and should return a boolean.
        388 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        389 * {@code f}.
        390 * @return {boolean} true if every value passes the test.
        391 * @template THIS, VALUE
        392 */
        393goog.iter.every = function(iterable, f, opt_obj) {
        394 iterable = goog.iter.toIterator(iterable);
        395 /** @preserveTry */
        396 try {
        397 while (true) {
        398 if (!f.call(opt_obj, iterable.next(), undefined, iterable)) {
        399 return false;
        400 }
        401 }
        402 } catch (ex) {
        403 if (ex !== goog.iter.StopIteration) {
        404 throw ex;
        405 }
        406 }
        407 return true;
        408};
        409
        410
        411/**
        412 * Takes zero or more iterables and returns one iterator that will iterate over
        413 * them in the order chained.
        414 * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
        415 * number of iterable objects.
        416 * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
        417 * iterate over all the given iterables' contents.
        418 * @template VALUE
        419 */
        420goog.iter.chain = function(var_args) {
        421 return goog.iter.chainFromIterable(arguments);
        422};
        423
        424
        425/**
        426 * Takes a single iterable containing zero or more iterables and returns one
        427 * iterator that will iterate over each one in the order given.
        428 * @see http://docs.python.org/2/library/itertools.html#itertools.chain.from_iterable
        429 * @param {goog.iter.Iterable} iterable The iterable of iterables to chain.
        430 * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
        431 * iterate over all the contents of the iterables contained within
        432 * {@code iterable}.
        433 * @template VALUE
        434 */
        435goog.iter.chainFromIterable = function(iterable) {
        436 var iterator = goog.iter.toIterator(iterable);
        437 var iter = new goog.iter.Iterator();
        438 var current = null;
        439
        440 iter.next = function() {
        441 while (true) {
        442 if (current == null) {
        443 var it = iterator.next();
        444 current = goog.iter.toIterator(it);
        445 }
        446 try {
        447 return current.next();
        448 } catch (ex) {
        449 if (ex !== goog.iter.StopIteration) {
        450 throw ex;
        451 }
        452 current = null;
        453 }
        454 }
        455 };
        456
        457 return iter;
        458};
        459
        460
        461/**
        462 * Builds a new iterator that iterates over the original, but skips elements as
        463 * long as a supplied function returns true.
        464 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        465 * object.
        466 * @param {
        467 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
        468 * The function to call for every value. This function takes 3 arguments
        469 * (the value, undefined, and the iterator) and should return a boolean.
        470 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        471 * {@code f}.
        472 * @return {!goog.iter.Iterator<VALUE>} A new iterator that drops elements from
        473 * the original iterator as long as {@code f} is true.
        474 * @template THIS, VALUE
        475 */
        476goog.iter.dropWhile = function(iterable, f, opt_obj) {
        477 var iterator = goog.iter.toIterator(iterable);
        478 var newIter = new goog.iter.Iterator;
        479 var dropping = true;
        480 newIter.next = function() {
        481 while (true) {
        482 var val = iterator.next();
        483 if (dropping && f.call(opt_obj, val, undefined, iterator)) {
        484 continue;
        485 } else {
        486 dropping = false;
        487 }
        488 return val;
        489 }
        490 };
        491 return newIter;
        492};
        493
        494
        495/**
        496 * Builds a new iterator that iterates over the original, but only as long as a
        497 * supplied function returns true.
        498 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        499 * object.
        500 * @param {
        501 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
        502 * The function to call for every value. This function takes 3 arguments
        503 * (the value, undefined, and the iterator) and should return a boolean.
        504 * @param {THIS=} opt_obj This is used as the 'this' object in f when called.
        505 * @return {!goog.iter.Iterator<VALUE>} A new iterator that keeps elements in
        506 * the original iterator as long as the function is true.
        507 * @template THIS, VALUE
        508 */
        509goog.iter.takeWhile = function(iterable, f, opt_obj) {
        510 var iterator = goog.iter.toIterator(iterable);
        511 var iter = new goog.iter.Iterator();
        512 iter.next = function() {
        513 var val = iterator.next();
        514 if (f.call(opt_obj, val, undefined, iterator)) {
        515 return val;
        516 }
        517 throw goog.iter.StopIteration;
        518 };
        519 return iter;
        520};
        521
        522
        523/**
        524 * Converts the iterator to an array
        525 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
        526 * to convert to an array.
        527 * @return {!Array<VALUE>} An array of the elements the iterator iterates over.
        528 * @template VALUE
        529 */
        530goog.iter.toArray = function(iterable) {
        531 // Fast path for array-like.
        532 if (goog.isArrayLike(iterable)) {
        533 return goog.array.toArray(/** @type {!goog.array.ArrayLike} */(iterable));
        534 }
        535 iterable = goog.iter.toIterator(iterable);
        536 var array = [];
        537 goog.iter.forEach(iterable, function(val) {
        538 array.push(val);
        539 });
        540 return array;
        541};
        542
        543
        544/**
        545 * Iterates over two iterables and returns true if they contain the same
        546 * sequence of elements and have the same length.
        547 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable1 The first
        548 * iterable object.
        549 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable2 The second
        550 * iterable object.
        551 * @param {function(VALUE,VALUE):boolean=} opt_equalsFn Optional comparison
        552 * function.
        553 * Should take two arguments to compare, and return true if the arguments
        554 * are equal. Defaults to {@link goog.array.defaultCompareEquality} which
        555 * compares the elements using the built-in '===' operator.
        556 * @return {boolean} true if the iterables contain the same sequence of elements
        557 * and have the same length.
        558 * @template VALUE
        559 */
        560goog.iter.equals = function(iterable1, iterable2, opt_equalsFn) {
        561 var fillValue = {};
        562 var pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2);
        563 var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
        564 return goog.iter.every(pairs, function(pair) {
        565 return equalsFn(pair[0], pair[1]);
        566 });
        567};
        568
        569
        570/**
        571 * Advances the iterator to the next position, returning the given default value
        572 * instead of throwing an exception if the iterator has no more entries.
        573 * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterable
        574 * object.
        575 * @param {VALUE} defaultValue The value to return if the iterator is empty.
        576 * @return {VALUE} The next item in the iteration, or defaultValue if the
        577 * iterator was empty.
        578 * @template VALUE
        579 */
        580goog.iter.nextOrValue = function(iterable, defaultValue) {
        581 try {
        582 return goog.iter.toIterator(iterable).next();
        583 } catch (e) {
        584 if (e != goog.iter.StopIteration) {
        585 throw e;
        586 }
        587 return defaultValue;
        588 }
        589};
        590
        591
        592/**
        593 * Cartesian product of zero or more sets. Gives an iterator that gives every
        594 * combination of one element chosen from each set. For example,
        595 * ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).
        596 * @see http://docs.python.org/library/itertools.html#itertools.product
        597 * @param {...!goog.array.ArrayLike<VALUE>} var_args Zero or more sets, as
        598 * arrays.
        599 * @return {!goog.iter.Iterator<!Array<VALUE>>} An iterator that gives each
        600 * n-tuple (as an array).
        601 * @template VALUE
        602 */
        603goog.iter.product = function(var_args) {
        604 var someArrayEmpty = goog.array.some(arguments, function(arr) {
        605 return !arr.length;
        606 });
        607
        608 // An empty set in a cartesian product gives an empty set.
        609 if (someArrayEmpty || !arguments.length) {
        610 return new goog.iter.Iterator();
        611 }
        612
        613 var iter = new goog.iter.Iterator();
        614 var arrays = arguments;
        615
        616 // The first indices are [0, 0, ...]
        617 var indicies = goog.array.repeat(0, arrays.length);
        618
        619 iter.next = function() {
        620
        621 if (indicies) {
        622 var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) {
        623 return arrays[arrayIndex][valueIndex];
        624 });
        625
        626 // Generate the next-largest indices for the next call.
        627 // Increase the rightmost index. If it goes over, increase the next
        628 // rightmost (like carry-over addition).
        629 for (var i = indicies.length - 1; i >= 0; i--) {
        630 // Assertion prevents compiler warning below.
        631 goog.asserts.assert(indicies);
        632 if (indicies[i] < arrays[i].length - 1) {
        633 indicies[i]++;
        634 break;
        635 }
        636
        637 // We're at the last indices (the last element of every array), so
        638 // the iteration is over on the next call.
        639 if (i == 0) {
        640 indicies = null;
        641 break;
        642 }
        643 // Reset the index in this column and loop back to increment the
        644 // next one.
        645 indicies[i] = 0;
        646 }
        647 return retVal;
        648 }
        649
        650 throw goog.iter.StopIteration;
        651 };
        652
        653 return iter;
        654};
        655
        656
        657/**
        658 * Create an iterator to cycle over the iterable's elements indefinitely.
        659 * For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...
        660 * @see: http://docs.python.org/library/itertools.html#itertools.cycle.
        661 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        662 * iterable object.
        663 * @return {!goog.iter.Iterator<VALUE>} An iterator that iterates indefinitely
        664 * over the values in {@code iterable}.
        665 * @template VALUE
        666 */
        667goog.iter.cycle = function(iterable) {
        668 var baseIterator = goog.iter.toIterator(iterable);
        669
        670 // We maintain a cache to store the iterable elements as we iterate
        671 // over them. The cache is used to return elements once we have
        672 // iterated over the iterable once.
        673 var cache = [];
        674 var cacheIndex = 0;
        675
        676 var iter = new goog.iter.Iterator();
        677
        678 // This flag is set after the iterable is iterated over once
        679 var useCache = false;
        680
        681 iter.next = function() {
        682 var returnElement = null;
        683
        684 // Pull elements off the original iterator if not using cache
        685 if (!useCache) {
        686 try {
        687 // Return the element from the iterable
        688 returnElement = baseIterator.next();
        689 cache.push(returnElement);
        690 return returnElement;
        691 } catch (e) {
        692 // If an exception other than StopIteration is thrown
        693 // or if there are no elements to iterate over (the iterable was empty)
        694 // throw an exception
        695 if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) {
        696 throw e;
        697 }
        698 // set useCache to true after we know that a 'StopIteration' exception
        699 // was thrown and the cache is not empty (to handle the 'empty iterable'
        700 // use case)
        701 useCache = true;
        702 }
        703 }
        704
        705 returnElement = cache[cacheIndex];
        706 cacheIndex = (cacheIndex + 1) % cache.length;
        707
        708 return returnElement;
        709 };
        710
        711 return iter;
        712};
        713
        714
        715/**
        716 * Creates an iterator that counts indefinitely from a starting value.
        717 * @see http://docs.python.org/2/library/itertools.html#itertools.count
        718 * @param {number=} opt_start The starting value. Default is 0.
        719 * @param {number=} opt_step The number to increment with between each call to
        720 * next. Negative and floating point numbers are allowed. Default is 1.
        721 * @return {!goog.iter.Iterator<number>} A new iterator that returns the values
        722 * in the series.
        723 */
        724goog.iter.count = function(opt_start, opt_step) {
        725 var counter = opt_start || 0;
        726 var step = goog.isDef(opt_step) ? opt_step : 1;
        727 var iter = new goog.iter.Iterator();
        728
        729 iter.next = function() {
        730 var returnValue = counter;
        731 counter += step;
        732 return returnValue;
        733 };
        734
        735 return iter;
        736};
        737
        738
        739/**
        740 * Creates an iterator that returns the same object or value repeatedly.
        741 * @param {VALUE} value Any object or value to repeat.
        742 * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
        743 * repeated value.
        744 * @template VALUE
        745 */
        746goog.iter.repeat = function(value) {
        747 var iter = new goog.iter.Iterator();
        748
        749 iter.next = goog.functions.constant(value);
        750
        751 return iter;
        752};
        753
        754
        755/**
        756 * Creates an iterator that returns running totals from the numbers in
        757 * {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields
        758 * {@code 1 -> 3 -> 6 -> 10 -> 15}.
        759 * @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate
        760 * @param {!goog.iter.Iterable<number>} iterable The iterable of numbers to
        761 * accumulate.
        762 * @return {!goog.iter.Iterator<number>} A new iterator that returns the
        763 * numbers in the series.
        764 */
        765goog.iter.accumulate = function(iterable) {
        766 var iterator = goog.iter.toIterator(iterable);
        767 var total = 0;
        768 var iter = new goog.iter.Iterator();
        769
        770 iter.next = function() {
        771 total += iterator.next();
        772 return total;
        773 };
        774
        775 return iter;
        776};
        777
        778
        779/**
        780 * Creates an iterator that returns arrays containing the ith elements from the
        781 * provided iterables. The returned arrays will be the same size as the number
        782 * of iterables given in {@code var_args}. Once the shortest iterable is
        783 * exhausted, subsequent calls to {@code next()} will throw
        784 * {@code goog.iter.StopIteration}.
        785 * @see http://docs.python.org/2/library/itertools.html#itertools.izip
        786 * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
        787 * number of iterable objects.
        788 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
        789 * arrays of elements from the provided iterables.
        790 * @template VALUE
        791 */
        792goog.iter.zip = function(var_args) {
        793 var args = arguments;
        794 var iter = new goog.iter.Iterator();
        795
        796 if (args.length > 0) {
        797 var iterators = goog.array.map(args, goog.iter.toIterator);
        798 iter.next = function() {
        799 var arr = goog.array.map(iterators, function(it) {
        800 return it.next();
        801 });
        802 return arr;
        803 };
        804 }
        805
        806 return iter;
        807};
        808
        809
        810/**
        811 * Creates an iterator that returns arrays containing the ith elements from the
        812 * provided iterables. The returned arrays will be the same size as the number
        813 * of iterables given in {@code var_args}. Shorter iterables will be extended
        814 * with {@code fillValue}. Once the longest iterable is exhausted, subsequent
        815 * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
        816 * @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest
        817 * @param {VALUE} fillValue The object or value used to fill shorter iterables.
        818 * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
        819 * number of iterable objects.
        820 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
        821 * arrays of elements from the provided iterables.
        822 * @template VALUE
        823 */
        824goog.iter.zipLongest = function(fillValue, var_args) {
        825 var args = goog.array.slice(arguments, 1);
        826 var iter = new goog.iter.Iterator();
        827
        828 if (args.length > 0) {
        829 var iterators = goog.array.map(args, goog.iter.toIterator);
        830
        831 iter.next = function() {
        832 var iteratorsHaveValues = false; // false when all iterators are empty.
        833 var arr = goog.array.map(iterators, function(it) {
        834 var returnValue;
        835 try {
        836 returnValue = it.next();
        837 // Iterator had a value, so we've not exhausted the iterators.
        838 // Set flag accordingly.
        839 iteratorsHaveValues = true;
        840 } catch (ex) {
        841 if (ex !== goog.iter.StopIteration) {
        842 throw ex;
        843 }
        844 returnValue = fillValue;
        845 }
        846 return returnValue;
        847 });
        848
        849 if (!iteratorsHaveValues) {
        850 throw goog.iter.StopIteration;
        851 }
        852 return arr;
        853 };
        854 }
        855
        856 return iter;
        857};
        858
        859
        860/**
        861 * Creates an iterator that filters {@code iterable} based on a series of
        862 * {@code selectors}. On each call to {@code next()}, one item is taken from
        863 * both the {@code iterable} and {@code selectors} iterators. If the item from
        864 * {@code selectors} evaluates to true, the item from {@code iterable} is given.
        865 * Otherwise, it is skipped. Once either {@code iterable} or {@code selectors}
        866 * is exhausted, subsequent calls to {@code next()} will throw
        867 * {@code goog.iter.StopIteration}.
        868 * @see http://docs.python.org/2/library/itertools.html#itertools.compress
        869 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        870 * iterable to filter.
        871 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} selectors An
        872 * iterable of items to be evaluated in a boolean context to determine if
        873 * the corresponding element in {@code iterable} should be included in the
        874 * result.
        875 * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
        876 * filtered values.
        877 * @template VALUE
        878 */
        879goog.iter.compress = function(iterable, selectors) {
        880 var selectorIterator = goog.iter.toIterator(selectors);
        881
        882 return goog.iter.filter(iterable, function() {
        883 return !!selectorIterator.next();
        884 });
        885};
        886
        887
        888
        889/**
        890 * Implements the {@code goog.iter.groupBy} iterator.
        891 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        892 * iterable to group.
        893 * @param {function(...VALUE): KEY=} opt_keyFunc Optional function for
        894 * determining the key value for each group in the {@code iterable}. Default
        895 * is the identity function.
        896 * @constructor
        897 * @extends {goog.iter.Iterator<!Array<?>>}
        898 * @template KEY, VALUE
        899 * @private
        900 */
        901goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {
        902
        903 /**
        904 * The iterable to group, coerced to an iterator.
        905 * @type {!goog.iter.Iterator}
        906 */
        907 this.iterator = goog.iter.toIterator(iterable);
        908
        909 /**
        910 * A function for determining the key value for each element in the iterable.
        911 * If no function is provided, the identity function is used and returns the
        912 * element unchanged.
        913 * @type {function(...VALUE): KEY}
        914 */
        915 this.keyFunc = opt_keyFunc || goog.functions.identity;
        916
        917 /**
        918 * The target key for determining the start of a group.
        919 * @type {KEY}
        920 */
        921 this.targetKey;
        922
        923 /**
        924 * The current key visited during iteration.
        925 * @type {KEY}
        926 */
        927 this.currentKey;
        928
        929 /**
        930 * The current value being added to the group.
        931 * @type {VALUE}
        932 */
        933 this.currentValue;
        934};
        935goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator);
        936
        937
        938/** @override */
        939goog.iter.GroupByIterator_.prototype.next = function() {
        940 while (this.currentKey == this.targetKey) {
        941 this.currentValue = this.iterator.next(); // Exits on StopIteration
        942 this.currentKey = this.keyFunc(this.currentValue);
        943 }
        944 this.targetKey = this.currentKey;
        945 return [this.currentKey, this.groupItems_(this.targetKey)];
        946};
        947
        948
        949/**
        950 * Performs the grouping of objects using the given key.
        951 * @param {KEY} targetKey The target key object for the group.
        952 * @return {!Array<VALUE>} An array of grouped objects.
        953 * @private
        954 */
        955goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) {
        956 var arr = [];
        957 while (this.currentKey == targetKey) {
        958 arr.push(this.currentValue);
        959 try {
        960 this.currentValue = this.iterator.next();
        961 } catch (ex) {
        962 if (ex !== goog.iter.StopIteration) {
        963 throw ex;
        964 }
        965 break;
        966 }
        967 this.currentKey = this.keyFunc(this.currentValue);
        968 }
        969 return arr;
        970};
        971
        972
        973/**
        974 * Creates an iterator that returns arrays containing elements from the
        975 * {@code iterable} grouped by a key value. For iterables with repeated
        976 * elements (i.e. sorted according to a particular key function), this function
        977 * has a {@code uniq}-like effect. For example, grouping the array:
        978 * {@code [A, B, B, C, C, A]} produces
        979 * {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}.
        980 * @see http://docs.python.org/2/library/itertools.html#itertools.groupby
        981 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        982 * iterable to group.
        983 * @param {function(...VALUE): KEY=} opt_keyFunc Optional function for
        984 * determining the key value for each group in the {@code iterable}. Default
        985 * is the identity function.
        986 * @return {!goog.iter.Iterator<!Array<?>>} A new iterator that returns
        987 * arrays of consecutive key and groups.
        988 * @template KEY, VALUE
        989 */
        990goog.iter.groupBy = function(iterable, opt_keyFunc) {
        991 return new goog.iter.GroupByIterator_(iterable, opt_keyFunc);
        992};
        993
        994
        995/**
        996 * Gives an iterator that gives the result of calling the given function
        997 * <code>f</code> with the arguments taken from the next element from
        998 * <code>iterable</code> (the elements are expected to also be iterables).
        999 *
        1000 * Similar to {@see goog.iter#map} but allows the function to accept multiple
        1001 * arguments from the iterable.
        1002 *
        1003 * @param {!goog.iter.Iterable<!goog.iter.Iterable>} iterable The iterable of
        1004 * iterables to iterate over.
        1005 * @param {function(this:THIS,...*):RESULT} f The function to call for every
        1006 * element. This function takes N+2 arguments, where N represents the
        1007 * number of items from the next element of the iterable. The two
        1008 * additional arguments passed to the function are undefined and the
        1009 * iterator itself. The function should return a new value.
        1010 * @param {THIS=} opt_obj The object to be used as the value of 'this' within
        1011 * {@code f}.
        1012 * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
        1013 * results of applying the function to each element in the original
        1014 * iterator.
        1015 * @template THIS, RESULT
        1016 */
        1017goog.iter.starMap = function(iterable, f, opt_obj) {
        1018 var iterator = goog.iter.toIterator(iterable);
        1019 var iter = new goog.iter.Iterator();
        1020
        1021 iter.next = function() {
        1022 var args = goog.iter.toArray(iterator.next());
        1023 return f.apply(opt_obj, goog.array.concat(args, undefined, iterator));
        1024 };
        1025
        1026 return iter;
        1027};
        1028
        1029
        1030/**
        1031 * Returns an array of iterators each of which can iterate over the values in
        1032 * {@code iterable} without advancing the others.
        1033 * @see http://docs.python.org/2/library/itertools.html#itertools.tee
        1034 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1035 * iterable to tee.
        1036 * @param {number=} opt_num The number of iterators to create. Default is 2.
        1037 * @return {!Array<goog.iter.Iterator<VALUE>>} An array of iterators.
        1038 * @template VALUE
        1039 */
        1040goog.iter.tee = function(iterable, opt_num) {
        1041 var iterator = goog.iter.toIterator(iterable);
        1042 var num = goog.isNumber(opt_num) ? opt_num : 2;
        1043 var buffers = goog.array.map(goog.array.range(num), function() {
        1044 return [];
        1045 });
        1046
        1047 var addNextIteratorValueToBuffers = function() {
        1048 var val = iterator.next();
        1049 goog.array.forEach(buffers, function(buffer) {
        1050 buffer.push(val);
        1051 });
        1052 };
        1053
        1054 var createIterator = function(buffer) {
        1055 // Each tee'd iterator has an associated buffer (initially empty). When a
        1056 // tee'd iterator's buffer is empty, it calls
        1057 // addNextIteratorValueToBuffers(), adding the next value to all tee'd
        1058 // iterators' buffers, and then returns that value. This allows each
        1059 // iterator to be advanced independently.
        1060 var iter = new goog.iter.Iterator();
        1061
        1062 iter.next = function() {
        1063 if (goog.array.isEmpty(buffer)) {
        1064 addNextIteratorValueToBuffers();
        1065 }
        1066 goog.asserts.assert(!goog.array.isEmpty(buffer));
        1067 return buffer.shift();
        1068 };
        1069
        1070 return iter;
        1071 };
        1072
        1073 return goog.array.map(buffers, createIterator);
        1074};
        1075
        1076
        1077/**
        1078 * Creates an iterator that returns arrays containing a count and an element
        1079 * obtained from the given {@code iterable}.
        1080 * @see http://docs.python.org/2/library/functions.html#enumerate
        1081 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1082 * iterable to enumerate.
        1083 * @param {number=} opt_start Optional starting value. Default is 0.
        1084 * @return {!goog.iter.Iterator<!Array<?>>} A new iterator containing
        1085 * count/item pairs.
        1086 * @template VALUE
        1087 */
        1088goog.iter.enumerate = function(iterable, opt_start) {
        1089 return goog.iter.zip(goog.iter.count(opt_start), iterable);
        1090};
        1091
        1092
        1093/**
        1094 * Creates an iterator that returns the first {@code limitSize} elements from an
        1095 * iterable. If this number is greater than the number of elements in the
        1096 * iterable, all the elements are returned.
        1097 * @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava.
        1098 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1099 * iterable to limit.
        1100 * @param {number} limitSize The maximum number of elements to return.
        1101 * @return {!goog.iter.Iterator<VALUE>} A new iterator containing
        1102 * {@code limitSize} elements.
        1103 * @template VALUE
        1104 */
        1105goog.iter.limit = function(iterable, limitSize) {
        1106 goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0);
        1107
        1108 var iterator = goog.iter.toIterator(iterable);
        1109
        1110 var iter = new goog.iter.Iterator();
        1111 var remaining = limitSize;
        1112
        1113 iter.next = function() {
        1114 if (remaining-- > 0) {
        1115 return iterator.next();
        1116 }
        1117 throw goog.iter.StopIteration;
        1118 };
        1119
        1120 return iter;
        1121};
        1122
        1123
        1124/**
        1125 * Creates an iterator that is advanced {@code count} steps ahead. Consumed
        1126 * values are silently discarded. If {@code count} is greater than the number
        1127 * of elements in {@code iterable}, an empty iterator is returned. Subsequent
        1128 * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
        1129 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1130 * iterable to consume.
        1131 * @param {number} count The number of elements to consume from the iterator.
        1132 * @return {!goog.iter.Iterator<VALUE>} An iterator advanced zero or more steps
        1133 * ahead.
        1134 * @template VALUE
        1135 */
        1136goog.iter.consume = function(iterable, count) {
        1137 goog.asserts.assert(goog.math.isInt(count) && count >= 0);
        1138
        1139 var iterator = goog.iter.toIterator(iterable);
        1140
        1141 while (count-- > 0) {
        1142 goog.iter.nextOrValue(iterator, null);
        1143 }
        1144
        1145 return iterator;
        1146};
        1147
        1148
        1149/**
        1150 * Creates an iterator that returns a range of elements from an iterable.
        1151 * Similar to {@see goog.array#slice} but does not support negative indexes.
        1152 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1153 * iterable to slice.
        1154 * @param {number} start The index of the first element to return.
        1155 * @param {number=} opt_end The index after the last element to return. If
        1156 * defined, must be greater than or equal to {@code start}.
        1157 * @return {!goog.iter.Iterator<VALUE>} A new iterator containing a slice of
        1158 * the original.
        1159 * @template VALUE
        1160 */
        1161goog.iter.slice = function(iterable, start, opt_end) {
        1162 goog.asserts.assert(goog.math.isInt(start) && start >= 0);
        1163
        1164 var iterator = goog.iter.consume(iterable, start);
        1165
        1166 if (goog.isNumber(opt_end)) {
        1167 goog.asserts.assert(goog.math.isInt(opt_end) && opt_end >= start);
        1168 iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */);
        1169 }
        1170
        1171 return iterator;
        1172};
        1173
        1174
        1175/**
        1176 * Checks an array for duplicate elements.
        1177 * @param {Array<VALUE>|goog.array.ArrayLike} arr The array to check for
        1178 * duplicates.
        1179 * @return {boolean} True, if the array contains duplicates, false otherwise.
        1180 * @private
        1181 * @template VALUE
        1182 */
        1183// TODO(user): Consider moving this into goog.array as a public function.
        1184goog.iter.hasDuplicates_ = function(arr) {
        1185 var deduped = [];
        1186 goog.array.removeDuplicates(arr, deduped);
        1187 return arr.length != deduped.length;
        1188};
        1189
        1190
        1191/**
        1192 * Creates an iterator that returns permutations of elements in
        1193 * {@code iterable}.
        1194 *
        1195 * Permutations are obtained by taking the Cartesian product of
        1196 * {@code opt_length} iterables and filtering out those with repeated
        1197 * elements. For example, the permutations of {@code [1,2,3]} are
        1198 * {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}.
        1199 * @see http://docs.python.org/2/library/itertools.html#itertools.permutations
        1200 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1201 * iterable from which to generate permutations.
        1202 * @param {number=} opt_length Length of each permutation. If omitted, defaults
        1203 * to the length of {@code iterable}.
        1204 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing the
        1205 * permutations of {@code iterable}.
        1206 * @template VALUE
        1207 */
        1208goog.iter.permutations = function(iterable, opt_length) {
        1209 var elements = goog.iter.toArray(iterable);
        1210 var length = goog.isNumber(opt_length) ? opt_length : elements.length;
        1211
        1212 var sets = goog.array.repeat(elements, length);
        1213 var product = goog.iter.product.apply(undefined, sets);
        1214
        1215 return goog.iter.filter(product, function(arr) {
        1216 return !goog.iter.hasDuplicates_(arr);
        1217 });
        1218};
        1219
        1220
        1221/**
        1222 * Creates an iterator that returns combinations of elements from
        1223 * {@code iterable}.
        1224 *
        1225 * Combinations are obtained by taking the {@see goog.iter#permutations} of
        1226 * {@code iterable} and filtering those whose elements appear in the order they
        1227 * are encountered in {@code iterable}. For example, the 3-length combinations
        1228 * of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}.
        1229 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations
        1230 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1231 * iterable from which to generate combinations.
        1232 * @param {number} length The length of each combination.
        1233 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
        1234 * combinations from the {@code iterable}.
        1235 * @template VALUE
        1236 */
        1237goog.iter.combinations = function(iterable, length) {
        1238 var elements = goog.iter.toArray(iterable);
        1239 var indexes = goog.iter.range(elements.length);
        1240 var indexIterator = goog.iter.permutations(indexes, length);
        1241 // sortedIndexIterator will now give arrays of with the given length that
        1242 // indicate what indexes into "elements" should be returned on each iteration.
        1243 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
        1244 return goog.array.isSorted(arr);
        1245 });
        1246
        1247 var iter = new goog.iter.Iterator();
        1248
        1249 function getIndexFromElements(index) {
        1250 return elements[index];
        1251 }
        1252
        1253 iter.next = function() {
        1254 return goog.array.map(sortedIndexIterator.next(), getIndexFromElements);
        1255 };
        1256
        1257 return iter;
        1258};
        1259
        1260
        1261/**
        1262 * Creates an iterator that returns combinations of elements from
        1263 * {@code iterable}, with repeated elements possible.
        1264 *
        1265 * Combinations are obtained by taking the Cartesian product of {@code length}
        1266 * iterables and filtering those whose elements appear in the order they are
        1267 * encountered in {@code iterable}. For example, the 2-length combinations of
        1268 * {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}.
        1269 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations_with_replacement
        1270 * @see http://en.wikipedia.org/wiki/Combination#Number_of_combinations_with_repetition
        1271 * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
        1272 * iterable to combine.
        1273 * @param {number} length The length of each combination.
        1274 * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
        1275 * combinations from the {@code iterable}.
        1276 * @template VALUE
        1277 */
        1278goog.iter.combinationsWithReplacement = function(iterable, length) {
        1279 var elements = goog.iter.toArray(iterable);
        1280 var indexes = goog.array.range(elements.length);
        1281 var sets = goog.array.repeat(indexes, length);
        1282 var indexIterator = goog.iter.product.apply(undefined, sets);
        1283 // sortedIndexIterator will now give arrays of with the given length that
        1284 // indicate what indexes into "elements" should be returned on each iteration.
        1285 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
        1286 return goog.array.isSorted(arr);
        1287 });
        1288
        1289 var iter = new goog.iter.Iterator();
        1290
        1291 function getIndexFromElements(index) {
        1292 return elements[index];
        1293 }
        1294
        1295 iter.next = function() {
        1296 return goog.array.map(
        1297 /** @type {!Array<number>} */
        1298 (sortedIndexIterator.next()), getIndexFromElements);
        1299 };
        1300
        1301 return iter;
        1302};
        \ No newline at end of file diff --git a/docs/source/lib/goog/json/json.js.src.html b/docs/source/lib/goog/json/json.js.src.html index 3b84a07..8a8ecd9 100644 --- a/docs/source/lib/goog/json/json.js.src.html +++ b/docs/source/lib/goog/json/json.js.src.html @@ -1 +1 @@ -json.js

        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
        21goog.provide('goog.json');
        22goog.provide('goog.json.Serializer');
        23
        24
        25/**
        26 * Tests if a string is an invalid JSON string. This only ensures that we are
        27 * not using any invalid characters
        28 * @param {string} s The string to test.
        29 * @return {boolean} True if the input is a valid JSON string.
        30 * @private
        31 */
        32goog.json.isValid_ = function(s) {
        33 // All empty whitespace is not valid.
        34 if (/^\s*$/.test(s)) {
        35 return false;
        36 }
        37
        38 // This is taken from http://www.json.org/json2.js which is released to the
        39 // public domain.
        40 // Changes: We dissallow \u2028 Line separator and \u2029 Paragraph separator
        41 // inside strings. We also treat \u2028 and \u2029 as whitespace which they
        42 // are in the RFC but IE and Safari does not match \s to these so we need to
        43 // include them in the reg exps in all places where whitespace is allowed.
        44 // We allowed \x7f inside strings because some tools don't escape it,
        45 // e.g. http://www.json.org/java/org/json/JSONObject.java
        46
        47 // Parsing happens in three stages. In the first stage, we run the text
        48 // against regular expressions that look for non-JSON patterns. We are
        49 // especially concerned with '()' and 'new' because they can cause invocation,
        50 // and '=' because it can cause mutation. But just to be safe, we want to
        51 // reject all unexpected forms.
        52
        53 // We split the first stage into 4 regexp operations in order to work around
        54 // crippling inefficiencies in IE's and Safari's regexp engines. First we
        55 // replace all backslash pairs with '@' (a non-JSON character). Second, we
        56 // replace all simple value tokens with ']' characters. Third, we delete all
        57 // open brackets that follow a colon or comma or that begin the text. Finally,
        58 // we look to see that the remaining characters are only whitespace or ']' or
        59 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
        60
        61 // Don't make these static since they have the global flag.
        62 var backslashesRe = /\\["\\\/bfnrtu]/g;
        63 var simpleValuesRe =
        64 /"[^"\\\n\r\u2028\u2029\x00-\x08\x0a-\x1f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
        65 var openBracketsRe = /(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g;
        66 var remainderRe = /^[\],:{}\s\u2028\u2029]*$/;
        67
        68 return remainderRe.test(s.replace(backslashesRe, '@').
        69 replace(simpleValuesRe, ']').
        70 replace(openBracketsRe, ''));
        71};
        72
        73
        74/**
        75 * Parses a JSON string and returns the result. This throws an exception if
        76 * the string is an invalid JSON string.
        77 *
        78 * Note that this is very slow on large strings. If you trust the source of
        79 * the string then you should use unsafeParse instead.
        80 *
        81 * @param {*} s The JSON string to parse.
        82 * @return {Object} The object generated from the JSON string.
        83 */
        84goog.json.parse = function(s) {
        85 var o = String(s);
        86 if (goog.json.isValid_(o)) {
        87 /** @preserveTry */
        88 try {
        89 return /** @type {Object} */ (eval('(' + o + ')'));
        90 } catch (ex) {
        91 }
        92 }
        93 throw Error('Invalid JSON string: ' + o);
        94};
        95
        96
        97/**
        98 * Parses a JSON string and returns the result. This uses eval so it is open
        99 * to security issues and it should only be used if you trust the source.
        100 *
        101 * @param {string} s The JSON string to parse.
        102 * @return {Object} The object generated from the JSON string.
        103 */
        104goog.json.unsafeParse = function(s) {
        105 return /** @type {Object} */ (eval('(' + s + ')'));
        106};
        107
        108
        109/**
        110 * JSON replacer, as defined in Section 15.12.3 of the ES5 spec.
        111 *
        112 * TODO(nicksantos): Array should also be a valid replacer.
        113 *
        114 * @typedef {function(this:Object, string, *): *}
        115 */
        116goog.json.Replacer;
        117
        118
        119/**
        120 * JSON reviver, as defined in Section 15.12.2 of the ES5 spec.
        121 *
        122 * @typedef {function(this:Object, string, *): *}
        123 */
        124goog.json.Reviver;
        125
        126
        127/**
        128 * Serializes an object or a value to a JSON string.
        129 *
        130 * @param {*} object The object to serialize.
        131 * @param {?goog.json.Replacer=} opt_replacer A replacer function
        132 * called for each (key, value) pair that determines how the value
        133 * should be serialized. By defult, this just returns the value
        134 * and allows default serialization to kick in.
        135 * @throws Error if there are loops in the object graph.
        136 * @return {string} A JSON string representation of the input.
        137 */
        138goog.json.serialize = function(object, opt_replacer) {
        139 // NOTE(nicksantos): Currently, we never use JSON.stringify.
        140 //
        141 // The last time I evaluated this, JSON.stringify had subtle bugs and behavior
        142 // differences on all browsers, and the performance win was not large enough
        143 // to justify all the issues. This may change in the future as browser
        144 // implementations get better.
        145 //
        146 // assertSerialize in json_test contains if branches for the cases
        147 // that fail.
        148 return new goog.json.Serializer(opt_replacer).serialize(object);
        149};
        150
        151
        152
        153/**
        154 * Class that is used to serialize JSON objects to a string.
        155 * @param {?goog.json.Replacer=} opt_replacer Replacer.
        156 * @constructor
        157 */
        158goog.json.Serializer = function(opt_replacer) {
        159 /**
        160 * @type {goog.json.Replacer|null|undefined}
        161 * @private
        162 */
        163 this.replacer_ = opt_replacer;
        164};
        165
        166
        167/**
        168 * Serializes an object or a value to a JSON string.
        169 *
        170 * @param {*} object The object to serialize.
        171 * @throws Error if there are loops in the object graph.
        172 * @return {string} A JSON string representation of the input.
        173 */
        174goog.json.Serializer.prototype.serialize = function(object) {
        175 var sb = [];
        176 this.serialize_(object, sb);
        177 return sb.join('');
        178};
        179
        180
        181/**
        182 * Serializes a generic value to a JSON string
        183 * @private
        184 * @param {*} object The object to serialize.
        185 * @param {Array} sb Array used as a string builder.
        186 * @throws Error if there are loops in the object graph.
        187 */
        188goog.json.Serializer.prototype.serialize_ = function(object, sb) {
        189 switch (typeof object) {
        190 case 'string':
        191 this.serializeString_(/** @type {string} */ (object), sb);
        192 break;
        193 case 'number':
        194 this.serializeNumber_(/** @type {number} */ (object), sb);
        195 break;
        196 case 'boolean':
        197 sb.push(object);
        198 break;
        199 case 'undefined':
        200 sb.push('null');
        201 break;
        202 case 'object':
        203 if (object == null) {
        204 sb.push('null');
        205 break;
        206 }
        207 if (goog.isArray(object)) {
        208 this.serializeArray(/** @type {!Array} */ (object), sb);
        209 break;
        210 }
        211 // should we allow new String, new Number and new Boolean to be treated
        212 // as string, number and boolean? Most implementations do not and the
        213 // need is not very big
        214 this.serializeObject_(/** @type {Object} */ (object), sb);
        215 break;
        216 case 'function':
        217 // Skip functions.
        218 // TODO(user) Should we return something here?
        219 break;
        220 default:
        221 throw Error('Unknown type: ' + typeof object);
        222 }
        223};
        224
        225
        226/**
        227 * Character mappings used internally for goog.string.quote
        228 * @private
        229 * @type {Object}
        230 */
        231goog.json.Serializer.charToJsonCharCache_ = {
        232 '\"': '\\"',
        233 '\\': '\\\\',
        234 '/': '\\/',
        235 '\b': '\\b',
        236 '\f': '\\f',
        237 '\n': '\\n',
        238 '\r': '\\r',
        239 '\t': '\\t',
        240
        241 '\x0B': '\\u000b' // '\v' is not supported in JScript
        242};
        243
        244
        245/**
        246 * Regular expression used to match characters that need to be replaced.
        247 * The S60 browser has a bug where unicode characters are not matched by
        248 * regular expressions. The condition below detects such behaviour and
        249 * adjusts the regular expression accordingly.
        250 * @private
        251 * @type {RegExp}
        252 */
        253goog.json.Serializer.charsToReplace_ = /\uffff/.test('\uffff') ?
        254 /[\\\"\x00-\x1f\x7f-\uffff]/g : /[\\\"\x00-\x1f\x7f-\xff]/g;
        255
        256
        257/**
        258 * Serializes a string to a JSON string
        259 * @private
        260 * @param {string} s The string to serialize.
        261 * @param {Array} sb Array used as a string builder.
        262 */
        263goog.json.Serializer.prototype.serializeString_ = function(s, sb) {
        264 // The official JSON implementation does not work with international
        265 // characters.
        266 sb.push('"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {
        267 // caching the result improves performance by a factor 2-3
        268 if (c in goog.json.Serializer.charToJsonCharCache_) {
        269 return goog.json.Serializer.charToJsonCharCache_[c];
        270 }
        271
        272 var cc = c.charCodeAt(0);
        273 var rv = '\\u';
        274 if (cc < 16) {
        275 rv += '000';
        276 } else if (cc < 256) {
        277 rv += '00';
        278 } else if (cc < 4096) { // \u1000
        279 rv += '0';
        280 }
        281 return goog.json.Serializer.charToJsonCharCache_[c] = rv + cc.toString(16);
        282 }), '"');
        283};
        284
        285
        286/**
        287 * Serializes a number to a JSON string
        288 * @private
        289 * @param {number} n The number to serialize.
        290 * @param {Array} sb Array used as a string builder.
        291 */
        292goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {
        293 sb.push(isFinite(n) && !isNaN(n) ? n : 'null');
        294};
        295
        296
        297/**
        298 * Serializes an array to a JSON string
        299 * @param {Array} arr The array to serialize.
        300 * @param {Array} sb Array used as a string builder.
        301 * @protected
        302 */
        303goog.json.Serializer.prototype.serializeArray = function(arr, sb) {
        304 var l = arr.length;
        305 sb.push('[');
        306 var sep = '';
        307 for (var i = 0; i < l; i++) {
        308 sb.push(sep);
        309
        310 var value = arr[i];
        311 this.serialize_(
        312 this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,
        313 sb);
        314
        315 sep = ',';
        316 }
        317 sb.push(']');
        318};
        319
        320
        321/**
        322 * Serializes an object to a JSON string
        323 * @private
        324 * @param {Object} obj The object to serialize.
        325 * @param {Array} sb Array used as a string builder.
        326 */
        327goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {
        328 sb.push('{');
        329 var sep = '';
        330 for (var key in obj) {
        331 if (Object.prototype.hasOwnProperty.call(obj, key)) {
        332 var value = obj[key];
        333 // Skip functions.
        334 // TODO(ptucker) Should we return something for function properties?
        335 if (typeof value != 'function') {
        336 sb.push(sep);
        337 this.serializeString_(key, sb);
        338 sb.push(':');
        339
        340 this.serialize_(
        341 this.replacer_ ? this.replacer_.call(obj, key, value) : value,
        342 sb);
        343
        344 sep = ',';
        345 }
        346 }
        347 }
        348 sb.push('}');
        349};
        \ No newline at end of file +json.js

        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
        21goog.provide('goog.json');
        22goog.provide('goog.json.Replacer');
        23goog.provide('goog.json.Reviver');
        24goog.provide('goog.json.Serializer');
        25
        26
        27/**
        28 * @define {boolean} If true, use the native JSON parsing API.
        29 * NOTE(ruilopes): 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 */
        33goog.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 */
        42goog.json.isValid = function(s) {
        43 // All empty whitespace is not valid.
        44 if (/^\s*$/.test(s)) {
        45 return false;
        46 }
        47
        48 // This is taken from http://www.json.org/json2.js which is released to the
        49 // public domain.
        50 // Changes: We dissallow \u2028 Line separator and \u2029 Paragraph separator
        51 // inside strings. We also treat \u2028 and \u2029 as whitespace which they
        52 // are in the RFC but IE and Safari does not match \s to these so we need to
        53 // include them in the reg exps in all places where whitespace is allowed.
        54 // We allowed \x7f inside strings because some tools don't escape it,
        55 // e.g. http://www.json.org/java/org/json/JSONObject.java
        56
        57 // Parsing happens in three stages. In the first stage, we run the text
        58 // against regular expressions that look for non-JSON patterns. We are
        59 // especially concerned with '()' and 'new' because they can cause invocation,
        60 // and '=' because it can cause mutation. But just to be safe, we want to
        61 // reject all unexpected forms.
        62
        63 // We split the first stage into 4 regexp operations in order to work around
        64 // crippling inefficiencies in IE's and Safari's regexp engines. First we
        65 // replace all backslash pairs with '@' (a non-JSON character). Second, we
        66 // replace all simple value tokens with ']' characters. Third, we delete all
        67 // open brackets that follow a colon or comma or that begin the text. Finally,
        68 // we look to see that the remaining characters are only whitespace or ']' or
        69 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
        70
        71 // Don't make these static since they have the global flag.
        72 var backslashesRe = /\\["\\\/bfnrtu]/g;
        73 var simpleValuesRe =
        74 /"[^"\\\n\r\u2028\u2029\x00-\x08\x0a-\x1f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
        75 var openBracketsRe = /(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g;
        76 var remainderRe = /^[\],:{}\s\u2028\u2029]*$/;
        77
        78 return remainderRe.test(s.replace(backslashesRe, '@').
        79 replace(simpleValuesRe, ']').
        80 replace(openBracketsRe, ''));
        81};
        82
        83
        84/**
        85 * Parses a JSON string and returns the result. This throws an exception if
        86 * the string is an invalid JSON string.
        87 *
        88 * Note that this is very slow on large strings. If you trust the source of
        89 * the string then you should use unsafeParse instead.
        90 *
        91 * @param {*} s The JSON string to parse.
        92 * @throws Error if s is invalid JSON.
        93 * @return {Object} The object generated from the JSON string, or null.
        94 */
        95goog.json.parse = goog.json.USE_NATIVE_JSON ?
        96 /** @type {function(*):Object} */ (goog.global['JSON']['parse']) :
        97 function(s) {
        98 var o = String(s);
        99 if (goog.json.isValid(o)) {
        100 /** @preserveTry */
        101 try {
        102 return /** @type {Object} */ (eval('(' + o + ')'));
        103 } catch (ex) {
        104 }
        105 }
        106 throw Error('Invalid JSON string: ' + o);
        107 };
        108
        109
        110/**
        111 * Parses a JSON string and returns the result. This uses eval so it is open
        112 * to security issues and it should only be used if you trust the source.
        113 *
        114 * @param {string} s The JSON string to parse.
        115 * @return {Object} The object generated from the JSON string.
        116 */
        117goog.json.unsafeParse = goog.json.USE_NATIVE_JSON ?
        118 /** @type {function(string):Object} */ (goog.global['JSON']['parse']) :
        119 function(s) {
        120 return /** @type {Object} */ (eval('(' + s + ')'));
        121 };
        122
        123
        124/**
        125 * JSON replacer, as defined in Section 15.12.3 of the ES5 spec.
        126 * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3
        127 *
        128 * TODO(nicksantos): Array should also be a valid replacer.
        129 *
        130 * @typedef {function(this:Object, string, *): *}
        131 */
        132goog.json.Replacer;
        133
        134
        135/**
        136 * JSON reviver, as defined in Section 15.12.2 of the ES5 spec.
        137 * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3
        138 *
        139 * @typedef {function(this:Object, string, *): *}
        140 */
        141goog.json.Reviver;
        142
        143
        144/**
        145 * Serializes an object or a value to a JSON string.
        146 *
        147 * @param {*} object The object to serialize.
        148 * @param {?goog.json.Replacer=} opt_replacer A replacer function
        149 * called for each (key, value) pair that determines how the value
        150 * should be serialized. By defult, this just returns the value
        151 * and allows default serialization to kick in.
        152 * @throws Error if there are loops in the object graph.
        153 * @return {string} A JSON string representation of the input.
        154 */
        155goog.json.serialize = goog.json.USE_NATIVE_JSON ?
        156 /** @type {function(*, ?goog.json.Replacer=):string} */
        157 (goog.global['JSON']['stringify']) :
        158 function(object, opt_replacer) {
        159 // NOTE(nicksantos): Currently, we never use JSON.stringify.
        160 //
        161 // The last time I evaluated this, JSON.stringify had subtle bugs and
        162 // behavior differences on all browsers, and the performance win was not
        163 // large enough to justify all the issues. This may change in the future
        164 // as browser implementations get better.
        165 //
        166 // assertSerialize in json_test contains if branches for the cases
        167 // that fail.
        168 return new goog.json.Serializer(opt_replacer).serialize(object);
        169 };
        170
        171
        172
        173/**
        174 * Class that is used to serialize JSON objects to a string.
        175 * @param {?goog.json.Replacer=} opt_replacer Replacer.
        176 * @constructor
        177 */
        178goog.json.Serializer = function(opt_replacer) {
        179 /**
        180 * @type {goog.json.Replacer|null|undefined}
        181 * @private
        182 */
        183 this.replacer_ = opt_replacer;
        184};
        185
        186
        187/**
        188 * Serializes an object or a value to a JSON string.
        189 *
        190 * @param {*} object The object to serialize.
        191 * @throws Error if there are loops in the object graph.
        192 * @return {string} A JSON string representation of the input.
        193 */
        194goog.json.Serializer.prototype.serialize = function(object) {
        195 var sb = [];
        196 this.serializeInternal(object, sb);
        197 return sb.join('');
        198};
        199
        200
        201/**
        202 * Serializes a generic value to a JSON string
        203 * @protected
        204 * @param {*} object The object to serialize.
        205 * @param {Array<string>} sb Array used as a string builder.
        206 * @throws Error if there are loops in the object graph.
        207 */
        208goog.json.Serializer.prototype.serializeInternal = function(object, sb) {
        209 if (object == null) {
        210 // undefined == null so this branch covers undefined as well as null
        211 sb.push('null');
        212 return;
        213 }
        214
        215 if (typeof object == 'object') {
        216 if (goog.isArray(object)) {
        217 this.serializeArray(object, sb);
        218 return;
        219 } else if (object instanceof String ||
        220 object instanceof Number ||
        221 object instanceof Boolean) {
        222 object = object.valueOf();
        223 // Fall through to switch below.
        224 } else {
        225 this.serializeObject_(/** @type {Object} */ (object), sb);
        226 return;
        227 }
        228 }
        229
        230 switch (typeof object) {
        231 case 'string':
        232 this.serializeString_(object, sb);
        233 break;
        234 case 'number':
        235 this.serializeNumber_(object, sb);
        236 break;
        237 case 'boolean':
        238 sb.push(String(object));
        239 break;
        240 case 'function':
        241 sb.push('null');
        242 break;
        243 default:
        244 throw Error('Unknown type: ' + typeof object);
        245 }
        246};
        247
        248
        249/**
        250 * Character mappings used internally for goog.string.quote
        251 * @private
        252 * @type {!Object}
        253 */
        254goog.json.Serializer.charToJsonCharCache_ = {
        255 '\"': '\\"',
        256 '\\': '\\\\',
        257 '/': '\\/',
        258 '\b': '\\b',
        259 '\f': '\\f',
        260 '\n': '\\n',
        261 '\r': '\\r',
        262 '\t': '\\t',
        263
        264 '\x0B': '\\u000b' // '\v' is not supported in JScript
        265};
        266
        267
        268/**
        269 * Regular expression used to match characters that need to be replaced.
        270 * The S60 browser has a bug where unicode characters are not matched by
        271 * regular expressions. The condition below detects such behaviour and
        272 * adjusts the regular expression accordingly.
        273 * @private
        274 * @type {!RegExp}
        275 */
        276goog.json.Serializer.charsToReplace_ = /\uffff/.test('\uffff') ?
        277 /[\\\"\x00-\x1f\x7f-\uffff]/g : /[\\\"\x00-\x1f\x7f-\xff]/g;
        278
        279
        280/**
        281 * Serializes a string to a JSON string
        282 * @private
        283 * @param {string} s The string to serialize.
        284 * @param {Array<string>} sb Array used as a string builder.
        285 */
        286goog.json.Serializer.prototype.serializeString_ = function(s, sb) {
        287 // The official JSON implementation does not work with international
        288 // characters.
        289 sb.push('"', s.replace(goog.json.Serializer.charsToReplace_, function(c) {
        290 // caching the result improves performance by a factor 2-3
        291 var rv = goog.json.Serializer.charToJsonCharCache_[c];
        292 if (!rv) {
        293 rv = '\\u' + (c.charCodeAt(0) | 0x10000).toString(16).substr(1);
        294 goog.json.Serializer.charToJsonCharCache_[c] = rv;
        295 }
        296 return rv;
        297 }), '"');
        298};
        299
        300
        301/**
        302 * Serializes a number to a JSON string
        303 * @private
        304 * @param {number} n The number to serialize.
        305 * @param {Array<string>} sb Array used as a string builder.
        306 */
        307goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) {
        308 sb.push(isFinite(n) && !isNaN(n) ? String(n) : 'null');
        309};
        310
        311
        312/**
        313 * Serializes an array to a JSON string
        314 * @param {Array<string>} arr The array to serialize.
        315 * @param {Array<string>} sb Array used as a string builder.
        316 * @protected
        317 */
        318goog.json.Serializer.prototype.serializeArray = function(arr, sb) {
        319 var l = arr.length;
        320 sb.push('[');
        321 var sep = '';
        322 for (var i = 0; i < l; i++) {
        323 sb.push(sep);
        324
        325 var value = arr[i];
        326 this.serializeInternal(
        327 this.replacer_ ? this.replacer_.call(arr, String(i), value) : value,
        328 sb);
        329
        330 sep = ',';
        331 }
        332 sb.push(']');
        333};
        334
        335
        336/**
        337 * Serializes an object to a JSON string
        338 * @private
        339 * @param {Object} obj The object to serialize.
        340 * @param {Array<string>} sb Array used as a string builder.
        341 */
        342goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) {
        343 sb.push('{');
        344 var sep = '';
        345 for (var key in obj) {
        346 if (Object.prototype.hasOwnProperty.call(obj, key)) {
        347 var value = obj[key];
        348 // Skip functions.
        349 if (typeof value != 'function') {
        350 sb.push(sep);
        351 this.serializeString_(key, sb);
        352 sb.push(':');
        353
        354 this.serializeInternal(
        355 this.replacer_ ? this.replacer_.call(obj, key, value) : value,
        356 sb);
        357
        358 sep = ',';
        359 }
        360 }
        361 }
        362 sb.push('}');
        363};
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/testing/assertthat.js.src.html b/docs/source/lib/goog/labs/testing/assertthat.js.src.html index cb81061..775f035 100644 --- a/docs/source/lib/goog/labs/testing/assertthat.js.src.html +++ b/docs/source/lib/goog/labs/testing/assertthat.js.src.html @@ -1 +1 @@ -assertthat.js

        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
        21goog.provide('goog.labs.testing.MatcherError');
        22goog.provide('goog.labs.testing.assertThat');
        23
        24goog.require('goog.asserts');
        25goog.require('goog.debug.Error');
        26goog.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 */
        37goog.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 */
        56goog.labs.testing.MatcherError = function(opt_message) {
        57 goog.base(this, opt_message);
        58};
        59goog.inherits(goog.labs.testing.MatcherError, goog.debug.Error);
        \ No newline at end of file +assertthat.js

        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
        21goog.provide('goog.labs.testing.MatcherError');
        22goog.provide('goog.labs.testing.assertThat');
        23
        24goog.require('goog.debug.Error');
        25
        26
        27/**
        28 * Asserts that the actual value evaluated by the matcher is true.
        29 *
        30 * @param {*} actual The object to assert by the matcher.
        31 * @param {!goog.labs.testing.Matcher} matcher A matcher to verify values.
        32 * @param {string=} opt_reason Description of what is asserted.
        33 *
        34 */
        35goog.labs.testing.assertThat = function(actual, matcher, opt_reason) {
        36 if (!matcher.matches(actual)) {
        37 // Prefix the error description with a reason from the assert ?
        38 var prefix = opt_reason ? opt_reason + ': ' : '';
        39 var desc = prefix + matcher.describe(actual);
        40
        41 // some sort of failure here
        42 throw new goog.labs.testing.MatcherError(desc);
        43 }
        44};
        45
        46
        47
        48/**
        49 * Error thrown when a Matcher fails to match the input value.
        50 * @param {string=} opt_message The error message.
        51 * @constructor
        52 * @extends {goog.debug.Error}
        53 * @final
        54 */
        55goog.labs.testing.MatcherError = function(opt_message) {
        56 goog.labs.testing.MatcherError.base(this, 'constructor', opt_message);
        57};
        58goog.inherits(goog.labs.testing.MatcherError, goog.debug.Error);
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/testing/logicmatcher.js.src.html b/docs/source/lib/goog/labs/testing/logicmatcher.js.src.html index 9aa4930..7ecfefb 100644 --- a/docs/source/lib/goog/labs/testing/logicmatcher.js.src.html +++ b/docs/source/lib/goog/labs/testing/logicmatcher.js.src.html @@ -1 +1 @@ -logicmatcher.js

        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
        21goog.provide('goog.labs.testing.AllOfMatcher');
        22goog.provide('goog.labs.testing.AnyOfMatcher');
        23goog.provide('goog.labs.testing.IsNotMatcher');
        24
        25
        26goog.require('goog.array');
        27goog.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 * @implements {goog.labs.testing.Matcher}
        38 */
        39goog.labs.testing.AllOfMatcher = function(matchers) {
        40 /**
        41 * @type {!Array.<!goog.labs.testing.Matcher>}
        42 * @private
        43 */
        44 this.matchers_ = matchers;
        45};
        46
        47
        48/**
        49 * Determines if all of the matchers match the input value.
        50 *
        51 * @override
        52 */
        53goog.labs.testing.AllOfMatcher.prototype.matches = function(actualValue) {
        54 return goog.array.every(this.matchers_, function(matcher) {
        55 return matcher.matches(actualValue);
        56 });
        57};
        58
        59
        60/**
        61 * Describes why the matcher failed. The returned string is a concatenation of
        62 * all the failed matchers' error strings.
        63 *
        64 * @override
        65 */
        66goog.labs.testing.AllOfMatcher.prototype.describe =
        67 function(actualValue) {
        68 // TODO(user) : Optimize this to remove duplication with matches ?
        69 var errorString = '';
        70 goog.array.forEach(this.matchers_, function(matcher) {
        71 if (!matcher.matches(actualValue)) {
        72 errorString += matcher.describe(actualValue) + '\n';
        73 }
        74 });
        75 return errorString;
        76};
        77
        78
        79
        80/**
        81 * The AnyOf matcher.
        82 *
        83 * @param {!Array.<!goog.labs.testing.Matcher>} matchers Input matchers.
        84 *
        85 * @constructor
        86 * @implements {goog.labs.testing.Matcher}
        87 */
        88goog.labs.testing.AnyOfMatcher = function(matchers) {
        89 /**
        90 * @type {!Array.<!goog.labs.testing.Matcher>}
        91 * @private
        92 */
        93 this.matchers_ = matchers;
        94};
        95
        96
        97/**
        98 * Determines if any of the matchers matches the input value.
        99 *
        100 * @override
        101 */
        102goog.labs.testing.AnyOfMatcher.prototype.matches = function(actualValue) {
        103 return goog.array.some(this.matchers_, function(matcher) {
        104 return matcher.matches(actualValue);
        105 });
        106};
        107
        108
        109/**
        110 * Describes why the matcher failed.
        111 *
        112 * @override
        113 */
        114goog.labs.testing.AnyOfMatcher.prototype.describe =
        115 function(actualValue) {
        116 // TODO(user) : Optimize this to remove duplication with matches ?
        117 var errorString = '';
        118 goog.array.forEach(this.matchers_, function(matcher) {
        119 if (!matcher.matches(actualValue)) {
        120 errorString += matcher.describe(actualValue) + '\n';
        121 }
        122 });
        123 return errorString;
        124};
        125
        126
        127
        128/**
        129 * The IsNot matcher.
        130 *
        131 * @param {!goog.labs.testing.Matcher} matcher The matcher to negate.
        132 *
        133 * @constructor
        134 * @implements {goog.labs.testing.Matcher}
        135 */
        136goog.labs.testing.IsNotMatcher = function(matcher) {
        137 /**
        138 * @type {!goog.labs.testing.Matcher}
        139 * @private
        140 */
        141 this.matcher_ = matcher;
        142};
        143
        144
        145/**
        146 * Determines if the input value doesn't satisfy a matcher.
        147 *
        148 * @override
        149 */
        150goog.labs.testing.IsNotMatcher.prototype.matches = function(actualValue) {
        151 return !this.matcher_.matches(actualValue);
        152};
        153
        154
        155/**
        156 * Describes why the matcher failed.
        157 *
        158 * @override
        159 */
        160goog.labs.testing.IsNotMatcher.prototype.describe =
        161 function(actualValue) {
        162 return 'The following is false: ' + this.matcher_.describe(actualValue);
        163};
        164
        165
        166/**
        167 * Creates a matcher that will succeed only if all of the given matchers
        168 * succeed.
        169 *
        170 * @param {...goog.labs.testing.Matcher} var_args The matchers to test
        171 * against.
        172 *
        173 * @return {!goog.labs.testing.AllOfMatcher} The AllOf matcher.
        174 */
        175function allOf(var_args) {
        176 var matchers = goog.array.toArray(arguments);
        177 return new goog.labs.testing.AllOfMatcher(matchers);
        178}
        179
        180
        181/**
        182 * Accepts a set of matchers and returns a matcher which matches
        183 * values which satisfy the constraints of any of the given matchers.
        184 *
        185 * @param {...goog.labs.testing.Matcher} var_args The matchers to test
        186 * against.
        187 *
        188 * @return {!goog.labs.testing.AnyOfMatcher} The AnyOf matcher.
        189 */
        190function anyOf(var_args) {
        191 var matchers = goog.array.toArray(arguments);
        192 return new goog.labs.testing.AnyOfMatcher(matchers);
        193}
        194
        195
        196/**
        197 * Returns a matcher that negates the input matcher. The returned
        198 * matcher matches the values not matched by the input matcher and vice-versa.
        199 *
        200 * @param {!goog.labs.testing.Matcher} matcher The matcher to test against.
        201 *
        202 * @return {!goog.labs.testing.IsNotMatcher} The IsNot matcher.
        203 */
        204function isNot(matcher) {
        205 return new goog.labs.testing.IsNotMatcher(matcher);
        206}
        \ No newline at end of file +logicmatcher.js

        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
        21goog.provide('goog.labs.testing.AllOfMatcher');
        22goog.provide('goog.labs.testing.AnyOfMatcher');
        23goog.provide('goog.labs.testing.IsNotMatcher');
        24
        25
        26goog.require('goog.array');
        27goog.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 */
        41goog.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 */
        55goog.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 */
        68goog.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 */
        92goog.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 */
        106goog.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 */
        118goog.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 */
        142goog.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 */
        156goog.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 */
        166goog.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 */
        181function 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 */
        196function 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 */
        210function isNot(matcher) {
        211 return new goog.labs.testing.IsNotMatcher(matcher);
        212}
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/testing/matcher.js.src.html b/docs/source/lib/goog/labs/testing/matcher.js.src.html index 2c148eb..0fa61c4 100644 --- a/docs/source/lib/goog/labs/testing/matcher.js.src.html +++ b/docs/source/lib/goog/labs/testing/matcher.js.src.html @@ -1 +1 @@ -matcher.js

        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
        21goog.provide('goog.labs.testing.Matcher');
        22
        23
        24
        25/**
        26 * A matcher object to be used in assertThat statements.
        27 * @interface
        28 */
        29goog.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 */
        38goog.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 */
        50goog.labs.testing.Matcher.prototype.describe =
        51 function(value, opt_description) {};
        \ No newline at end of file +matcher.js

        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
        21goog.provide('goog.labs.testing.Matcher');
        22
        23
        24
        25/**
        26 * A matcher object to be used in assertThat statements.
        27 * @interface
        28 */
        29goog.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 */
        38goog.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 */
        50goog.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 */
        61goog.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};
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/testing/numbermatcher.js.src.html b/docs/source/lib/goog/labs/testing/numbermatcher.js.src.html index d6a9293..c73dfcf 100644 --- a/docs/source/lib/goog/labs/testing/numbermatcher.js.src.html +++ b/docs/source/lib/goog/labs/testing/numbermatcher.js.src.html @@ -1 +1 @@ -numbermatcher.js

        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
        21goog.provide('goog.labs.testing.CloseToMatcher');
        22goog.provide('goog.labs.testing.EqualToMatcher');
        23goog.provide('goog.labs.testing.GreaterThanEqualToMatcher');
        24goog.provide('goog.labs.testing.GreaterThanMatcher');
        25goog.provide('goog.labs.testing.LessThanEqualToMatcher');
        26goog.provide('goog.labs.testing.LessThanMatcher');
        27
        28
        29goog.require('goog.asserts');
        30goog.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 * @implements {goog.labs.testing.Matcher}
        41 */
        42goog.labs.testing.GreaterThanMatcher = function(value) {
        43 /**
        44 * @type {number}
        45 * @private
        46 */
        47 this.value_ = value;
        48};
        49
        50
        51/**
        52 * Determines if input value is greater than the expected value.
        53 *
        54 * @override
        55 */
        56goog.labs.testing.GreaterThanMatcher.prototype.matches = function(actualValue) {
        57 goog.asserts.assertNumber(actualValue);
        58 return actualValue > this.value_;
        59};
        60
        61
        62/**
        63 * @override
        64 */
        65goog.labs.testing.GreaterThanMatcher.prototype.describe =
        66 function(actualValue) {
        67 goog.asserts.assertNumber(actualValue);
        68 return actualValue + ' is not greater than ' + this.value_;
        69};
        70
        71
        72
        73/**
        74 * The lessThan matcher.
        75 *
        76 * @param {number} value The value to compare.
        77 *
        78 * @constructor
        79 * @implements {goog.labs.testing.Matcher}
        80 */
        81goog.labs.testing.LessThanMatcher = function(value) {
        82 /**
        83 * @type {number}
        84 * @private
        85 */
        86 this.value_ = value;
        87};
        88
        89
        90/**
        91 * Determines if the input value is less than the expected value.
        92 *
        93 * @override
        94 */
        95goog.labs.testing.LessThanMatcher.prototype.matches = function(actualValue) {
        96 goog.asserts.assertNumber(actualValue);
        97 return actualValue < this.value_;
        98};
        99
        100
        101/**
        102 * @override
        103 */
        104goog.labs.testing.LessThanMatcher.prototype.describe =
        105 function(actualValue) {
        106 goog.asserts.assertNumber(actualValue);
        107 return actualValue + ' is not less than ' + this.value_;
        108};
        109
        110
        111
        112/**
        113 * The GreaterThanEqualTo matcher.
        114 *
        115 * @param {number} value The value to compare.
        116 *
        117 * @constructor
        118 * @implements {goog.labs.testing.Matcher}
        119 */
        120goog.labs.testing.GreaterThanEqualToMatcher = function(value) {
        121 /**
        122 * @type {number}
        123 * @private
        124 */
        125 this.value_ = value;
        126};
        127
        128
        129/**
        130 * Determines if the input value is greater than equal to the expected value.
        131 *
        132 * @override
        133 */
        134goog.labs.testing.GreaterThanEqualToMatcher.prototype.matches =
        135 function(actualValue) {
        136 goog.asserts.assertNumber(actualValue);
        137 return actualValue >= this.value_;
        138};
        139
        140
        141/**
        142 * @override
        143 */
        144goog.labs.testing.GreaterThanEqualToMatcher.prototype.describe =
        145 function(actualValue) {
        146 goog.asserts.assertNumber(actualValue);
        147 return actualValue + ' is not greater than equal to ' + this.value_;
        148};
        149
        150
        151
        152/**
        153 * The LessThanEqualTo matcher.
        154 *
        155 * @param {number} value The value to compare.
        156 *
        157 * @constructor
        158 * @implements {goog.labs.testing.Matcher}
        159 */
        160goog.labs.testing.LessThanEqualToMatcher = function(value) {
        161 /**
        162 * @type {number}
        163 * @private
        164 */
        165 this.value_ = value;
        166};
        167
        168
        169/**
        170 * Determines if the input value is less than or equal to the expected value.
        171 *
        172 * @override
        173 */
        174goog.labs.testing.LessThanEqualToMatcher.prototype.matches =
        175 function(actualValue) {
        176 goog.asserts.assertNumber(actualValue);
        177 return actualValue <= this.value_;
        178};
        179
        180
        181/**
        182 * @override
        183 */
        184goog.labs.testing.LessThanEqualToMatcher.prototype.describe =
        185 function(actualValue) {
        186 goog.asserts.assertNumber(actualValue);
        187 return actualValue + ' is not less than equal to ' + this.value_;
        188};
        189
        190
        191
        192/**
        193 * The EqualTo matcher.
        194 *
        195 * @param {number} value The value to compare.
        196 *
        197 * @constructor
        198 * @implements {goog.labs.testing.Matcher}
        199 */
        200goog.labs.testing.EqualToMatcher = function(value) {
        201 /**
        202 * @type {number}
        203 * @private
        204 */
        205 this.value_ = value;
        206};
        207
        208
        209/**
        210 * Determines if the input value is equal to the expected value.
        211 *
        212 * @override
        213 */
        214goog.labs.testing.EqualToMatcher.prototype.matches = function(actualValue) {
        215 goog.asserts.assertNumber(actualValue);
        216 return actualValue === this.value_;
        217};
        218
        219
        220/**
        221 * @override
        222 */
        223goog.labs.testing.EqualToMatcher.prototype.describe =
        224 function(actualValue) {
        225 goog.asserts.assertNumber(actualValue);
        226 return actualValue + ' is not equal to ' + this.value_;
        227};
        228
        229
        230
        231/**
        232 * The CloseTo matcher.
        233 *
        234 * @param {number} value The value to compare.
        235 * @param {number} range The range to check within.
        236 *
        237 * @constructor
        238 * @implements {goog.labs.testing.Matcher}
        239 */
        240goog.labs.testing.CloseToMatcher = function(value, range) {
        241 /**
        242 * @type {number}
        243 * @private
        244 */
        245 this.value_ = value;
        246 /**
        247 * @type {number}
        248 * @private
        249 */
        250 this.range_ = range;
        251};
        252
        253
        254/**
        255 * Determines if input value is within a certain range of the expected value.
        256 *
        257 * @override
        258 */
        259goog.labs.testing.CloseToMatcher.prototype.matches = function(actualValue) {
        260 goog.asserts.assertNumber(actualValue);
        261 return Math.abs(this.value_ - actualValue) < this.range_;
        262};
        263
        264
        265/**
        266 * @override
        267 */
        268goog.labs.testing.CloseToMatcher.prototype.describe =
        269 function(actualValue) {
        270 goog.asserts.assertNumber(actualValue);
        271 return actualValue + ' is not close to(' + this.range_ + ') ' + this.value_;
        272};
        273
        274
        275/**
        276 * @param {number} value The expected value.
        277 *
        278 * @return {!goog.labs.testing.GreaterThanMatcher} A GreaterThanMatcher.
        279 */
        280function greaterThan(value) {
        281 return new goog.labs.testing.GreaterThanMatcher(value);
        282}
        283
        284
        285/**
        286 * @param {number} value The expected value.
        287 *
        288 * @return {!goog.labs.testing.GreaterThanEqualToMatcher} A
        289 * GreaterThanEqualToMatcher.
        290 */
        291function greaterThanEqualTo(value) {
        292 return new goog.labs.testing.GreaterThanEqualToMatcher(value);
        293}
        294
        295
        296/**
        297 * @param {number} value The expected value.
        298 *
        299 * @return {!goog.labs.testing.LessThanMatcher} A LessThanMatcher.
        300 */
        301function lessThan(value) {
        302 return new goog.labs.testing.LessThanMatcher(value);
        303}
        304
        305
        306/**
        307 * @param {number} value The expected value.
        308 *
        309 * @return {!goog.labs.testing.LessThanEqualToMatcher} A LessThanEqualToMatcher.
        310 */
        311function lessThanEqualTo(value) {
        312 return new goog.labs.testing.LessThanEqualToMatcher(value);
        313}
        314
        315
        316/**
        317 * @param {number} value The expected value.
        318 *
        319 * @return {!goog.labs.testing.EqualToMatcher} An EqualToMatcher.
        320 */
        321function equalTo(value) {
        322 return new goog.labs.testing.EqualToMatcher(value);
        323}
        324
        325
        326/**
        327 * @param {number} value The expected value.
        328 * @param {number} range The maximum allowed difference from the expected value.
        329 *
        330 * @return {!goog.labs.testing.CloseToMatcher} A CloseToMatcher.
        331 */
        332function closeTo(value, range) {
        333 return new goog.labs.testing.CloseToMatcher(value, range);
        334}
        \ No newline at end of file +numbermatcher.js

        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
        21goog.provide('goog.labs.testing.CloseToMatcher');
        22goog.provide('goog.labs.testing.EqualToMatcher');
        23goog.provide('goog.labs.testing.GreaterThanEqualToMatcher');
        24goog.provide('goog.labs.testing.GreaterThanMatcher');
        25goog.provide('goog.labs.testing.LessThanEqualToMatcher');
        26goog.provide('goog.labs.testing.LessThanMatcher');
        27
        28
        29goog.require('goog.asserts');
        30goog.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 */
        44goog.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 */
        58goog.labs.testing.GreaterThanMatcher.prototype.matches = function(actualValue) {
        59 goog.asserts.assertNumber(actualValue);
        60 return actualValue > this.value_;
        61};
        62
        63
        64/**
        65 * @override
        66 */
        67goog.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 */
        85goog.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 */
        99goog.labs.testing.LessThanMatcher.prototype.matches = function(actualValue) {
        100 goog.asserts.assertNumber(actualValue);
        101 return actualValue < this.value_;
        102};
        103
        104
        105/**
        106 * @override
        107 */
        108goog.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 */
        126goog.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 */
        140goog.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 */
        150goog.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 */
        168goog.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 */
        182goog.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 */
        192goog.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 */
        210goog.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 */
        224goog.labs.testing.EqualToMatcher.prototype.matches = function(actualValue) {
        225 goog.asserts.assertNumber(actualValue);
        226 return actualValue === this.value_;
        227};
        228
        229
        230/**
        231 * @override
        232 */
        233goog.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 */
        252goog.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 */
        271goog.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 */
        280goog.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 */
        292function 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 */
        303function 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 */
        313function 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 */
        323function 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 */
        333function 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 */
        344function closeTo(value, range) {
        345 return new goog.labs.testing.CloseToMatcher(value, range);
        346}
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/testing/objectmatcher.js.src.html b/docs/source/lib/goog/labs/testing/objectmatcher.js.src.html index 69a7f81..f74e010 100644 --- a/docs/source/lib/goog/labs/testing/objectmatcher.js.src.html +++ b/docs/source/lib/goog/labs/testing/objectmatcher.js.src.html @@ -1 +1 @@ -objectmatcher.js

        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
        22goog.provide('goog.labs.testing.HasPropertyMatcher');
        23goog.provide('goog.labs.testing.InstanceOfMatcher');
        24goog.provide('goog.labs.testing.IsNullMatcher');
        25goog.provide('goog.labs.testing.IsNullOrUndefinedMatcher');
        26goog.provide('goog.labs.testing.IsUndefinedMatcher');
        27goog.provide('goog.labs.testing.ObjectEqualsMatcher');
        28
        29
        30goog.require('goog.labs.testing.Matcher');
        31goog.require('goog.string');
        32
        33
        34
        35/**
        36 * The Equals matcher.
        37 *
        38 * @param {!Object} expectedObject The expected object.
        39 *
        40 * @constructor
        41 * @implements {goog.labs.testing.Matcher}
        42 */
        43goog.labs.testing.ObjectEqualsMatcher = function(expectedObject) {
        44 /**
        45 * @type {!Object}
        46 * @private
        47 */
        48 this.object_ = expectedObject;
        49};
        50
        51
        52/**
        53 * Determines if two objects are the same.
        54 *
        55 * @override
        56 */
        57goog.labs.testing.ObjectEqualsMatcher.prototype.matches =
        58 function(actualObject) {
        59 return actualObject === this.object_;
        60};
        61
        62
        63/**
        64 * @override
        65 */
        66goog.labs.testing.ObjectEqualsMatcher.prototype.describe =
        67 function(actualObject) {
        68 return 'Input object is not the same as the expected object.';
        69};
        70
        71
        72
        73/**
        74 * The HasProperty matcher.
        75 *
        76 * @param {string} property Name of the property to test.
        77 *
        78 * @constructor
        79 * @implements {goog.labs.testing.Matcher}
        80 */
        81goog.labs.testing.HasPropertyMatcher = function(property) {
        82 /**
        83 * @type {string}
        84 * @private
        85 */
        86 this.property_ = property;
        87};
        88
        89
        90/**
        91 * Determines if an object has a property.
        92 *
        93 * @override
        94 */
        95goog.labs.testing.HasPropertyMatcher.prototype.matches =
        96 function(actualObject) {
        97 return this.property_ in actualObject;
        98};
        99
        100
        101/**
        102 * @override
        103 */
        104goog.labs.testing.HasPropertyMatcher.prototype.describe =
        105 function(actualObject) {
        106 return 'Object does not have property: ' + this.property_;
        107};
        108
        109
        110
        111/**
        112 * The InstanceOf matcher.
        113 *
        114 * @param {!Object} object The expected class object.
        115 *
        116 * @constructor
        117 * @implements {goog.labs.testing.Matcher}
        118 */
        119goog.labs.testing.InstanceOfMatcher = function(object) {
        120 /**
        121 * @type {!Object}
        122 * @private
        123 */
        124 this.object_ = object;
        125};
        126
        127
        128/**
        129 * Determines if an object is an instance of another object.
        130 *
        131 * @override
        132 */
        133goog.labs.testing.InstanceOfMatcher.prototype.matches =
        134 function(actualObject) {
        135 return actualObject instanceof this.object_;
        136};
        137
        138
        139/**
        140 * @override
        141 */
        142goog.labs.testing.InstanceOfMatcher.prototype.describe =
        143 function(actualObject) {
        144 return 'Input object is not an instance of the expected object';
        145};
        146
        147
        148
        149/**
        150 * The IsNullOrUndefined matcher.
        151 *
        152 * @constructor
        153 * @implements {goog.labs.testing.Matcher}
        154 */
        155goog.labs.testing.IsNullOrUndefinedMatcher = function() {};
        156
        157
        158/**
        159 * Determines if input value is null or undefined.
        160 *
        161 * @override
        162 */
        163goog.labs.testing.IsNullOrUndefinedMatcher.prototype.matches =
        164 function(actualValue) {
        165 return !goog.isDefAndNotNull(actualValue);
        166};
        167
        168
        169/**
        170 * @override
        171 */
        172goog.labs.testing.IsNullOrUndefinedMatcher.prototype.describe =
        173 function(actualValue) {
        174 return actualValue + ' is not null or undefined.';
        175};
        176
        177
        178
        179/**
        180 * The IsNull matcher.
        181 *
        182 * @constructor
        183 * @implements {goog.labs.testing.Matcher}
        184 */
        185goog.labs.testing.IsNullMatcher = function() {};
        186
        187
        188/**
        189 * Determines if input value is null.
        190 *
        191 * @override
        192 */
        193goog.labs.testing.IsNullMatcher.prototype.matches =
        194 function(actualValue) {
        195 return goog.isNull(actualValue);
        196};
        197
        198
        199/**
        200 * @override
        201 */
        202goog.labs.testing.IsNullMatcher.prototype.describe =
        203 function(actualValue) {
        204 return actualValue + ' is not null.';
        205};
        206
        207
        208
        209/**
        210 * The IsUndefined matcher.
        211 *
        212 * @constructor
        213 * @implements {goog.labs.testing.Matcher}
        214 */
        215goog.labs.testing.IsUndefinedMatcher = function() {};
        216
        217
        218/**
        219 * Determines if input value is undefined.
        220 *
        221 * @override
        222 */
        223goog.labs.testing.IsUndefinedMatcher.prototype.matches =
        224 function(actualValue) {
        225 return !goog.isDef(actualValue);
        226};
        227
        228
        229/**
        230 * @override
        231 */
        232goog.labs.testing.IsUndefinedMatcher.prototype.describe =
        233 function(actualValue) {
        234 return actualValue + ' is not undefined.';
        235};
        236
        237
        238/**
        239 * Returns a matcher that matches objects that are equal to the input object.
        240 * Equality in this case means the two objects are references to the same
        241 * object.
        242 *
        243 * @param {!Object} object The expected object.
        244 *
        245 * @return {!goog.labs.testing.ObjectEqualsMatcher} A
        246 * ObjectEqualsMatcher.
        247 */
        248function equalsObject(object) {
        249 return new goog.labs.testing.ObjectEqualsMatcher(object);
        250}
        251
        252
        253/**
        254 * Returns a matcher that matches objects that contain the input property.
        255 *
        256 * @param {string} property The property name to check.
        257 *
        258 * @return {!goog.labs.testing.HasPropertyMatcher} A HasPropertyMatcher.
        259 */
        260function hasProperty(property) {
        261 return new goog.labs.testing.HasPropertyMatcher(property);
        262}
        263
        264
        265/**
        266 * Returns a matcher that matches instances of the input class.
        267 *
        268 * @param {!Object} object The class object.
        269 *
        270 * @return {!goog.labs.testing.InstanceOfMatcher} A
        271 * InstanceOfMatcher.
        272 */
        273function instanceOfClass(object) {
        274 return new goog.labs.testing.InstanceOfMatcher(object);
        275}
        276
        277
        278/**
        279 * Returns a matcher that matches all null values.
        280 *
        281 * @return {!goog.labs.testing.IsNullMatcher} A IsNullMatcher.
        282 */
        283function isNull() {
        284 return new goog.labs.testing.IsNullMatcher();
        285}
        286
        287
        288/**
        289 * Returns a matcher that matches all null and undefined values.
        290 *
        291 * @return {!goog.labs.testing.IsNullOrUndefinedMatcher} A
        292 * IsNullOrUndefinedMatcher.
        293 */
        294function isNullOrUndefined() {
        295 return new goog.labs.testing.IsNullOrUndefinedMatcher();
        296}
        297
        298
        299/**
        300 * Returns a matcher that matches undefined values.
        301 *
        302 * @return {!goog.labs.testing.IsUndefinedMatcher} A IsUndefinedMatcher.
        303 */
        304function isUndefined() {
        305 return new goog.labs.testing.IsUndefinedMatcher();
        306}
        \ No newline at end of file +objectmatcher.js

        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
        22goog.provide('goog.labs.testing.HasPropertyMatcher');
        23goog.provide('goog.labs.testing.InstanceOfMatcher');
        24goog.provide('goog.labs.testing.IsNullMatcher');
        25goog.provide('goog.labs.testing.IsNullOrUndefinedMatcher');
        26goog.provide('goog.labs.testing.IsUndefinedMatcher');
        27goog.provide('goog.labs.testing.ObjectEqualsMatcher');
        28
        29
        30goog.require('goog.labs.testing.Matcher');
        31
        32
        33
        34/**
        35 * The Equals matcher.
        36 *
        37 * @param {!Object} expectedObject The expected object.
        38 *
        39 * @constructor
        40 * @struct
        41 * @implements {goog.labs.testing.Matcher}
        42 * @final
        43 */
        44goog.labs.testing.ObjectEqualsMatcher = function(expectedObject) {
        45 /**
        46 * @type {!Object}
        47 * @private
        48 */
        49 this.object_ = expectedObject;
        50};
        51
        52
        53/**
        54 * Determines if two objects are the same.
        55 *
        56 * @override
        57 */
        58goog.labs.testing.ObjectEqualsMatcher.prototype.matches =
        59 function(actualObject) {
        60 return actualObject === this.object_;
        61};
        62
        63
        64/**
        65 * @override
        66 */
        67goog.labs.testing.ObjectEqualsMatcher.prototype.describe =
        68 function(actualObject) {
        69 return 'Input object is not the same as the expected object.';
        70};
        71
        72
        73
        74/**
        75 * The HasProperty matcher.
        76 *
        77 * @param {string} property Name of the property to test.
        78 *
        79 * @constructor
        80 * @struct
        81 * @implements {goog.labs.testing.Matcher}
        82 * @final
        83 */
        84goog.labs.testing.HasPropertyMatcher = function(property) {
        85 /**
        86 * @type {string}
        87 * @private
        88 */
        89 this.property_ = property;
        90};
        91
        92
        93/**
        94 * Determines if an object has a property.
        95 *
        96 * @override
        97 */
        98goog.labs.testing.HasPropertyMatcher.prototype.matches =
        99 function(actualObject) {
        100 return this.property_ in actualObject;
        101};
        102
        103
        104/**
        105 * @override
        106 */
        107goog.labs.testing.HasPropertyMatcher.prototype.describe =
        108 function(actualObject) {
        109 return 'Object does not have property: ' + this.property_;
        110};
        111
        112
        113
        114/**
        115 * The InstanceOf matcher.
        116 *
        117 * @param {!Object} object The expected class object.
        118 *
        119 * @constructor
        120 * @struct
        121 * @implements {goog.labs.testing.Matcher}
        122 * @final
        123 */
        124goog.labs.testing.InstanceOfMatcher = function(object) {
        125 /**
        126 * @type {!Object}
        127 * @private
        128 */
        129 this.object_ = object;
        130};
        131
        132
        133/**
        134 * Determines if an object is an instance of another object.
        135 *
        136 * @override
        137 */
        138goog.labs.testing.InstanceOfMatcher.prototype.matches =
        139 function(actualObject) {
        140 return actualObject instanceof this.object_;
        141};
        142
        143
        144/**
        145 * @override
        146 */
        147goog.labs.testing.InstanceOfMatcher.prototype.describe =
        148 function(actualObject) {
        149 return 'Input object is not an instance of the expected object';
        150};
        151
        152
        153
        154/**
        155 * The IsNullOrUndefined matcher.
        156 *
        157 * @constructor
        158 * @struct
        159 * @implements {goog.labs.testing.Matcher}
        160 * @final
        161 */
        162goog.labs.testing.IsNullOrUndefinedMatcher = function() {};
        163
        164
        165/**
        166 * Determines if input value is null or undefined.
        167 *
        168 * @override
        169 */
        170goog.labs.testing.IsNullOrUndefinedMatcher.prototype.matches =
        171 function(actualValue) {
        172 return !goog.isDefAndNotNull(actualValue);
        173};
        174
        175
        176/**
        177 * @override
        178 */
        179goog.labs.testing.IsNullOrUndefinedMatcher.prototype.describe =
        180 function(actualValue) {
        181 return actualValue + ' is not null or undefined.';
        182};
        183
        184
        185
        186/**
        187 * The IsNull matcher.
        188 *
        189 * @constructor
        190 * @struct
        191 * @implements {goog.labs.testing.Matcher}
        192 * @final
        193 */
        194goog.labs.testing.IsNullMatcher = function() {};
        195
        196
        197/**
        198 * Determines if input value is null.
        199 *
        200 * @override
        201 */
        202goog.labs.testing.IsNullMatcher.prototype.matches =
        203 function(actualValue) {
        204 return goog.isNull(actualValue);
        205};
        206
        207
        208/**
        209 * @override
        210 */
        211goog.labs.testing.IsNullMatcher.prototype.describe =
        212 function(actualValue) {
        213 return actualValue + ' is not null.';
        214};
        215
        216
        217
        218/**
        219 * The IsUndefined matcher.
        220 *
        221 * @constructor
        222 * @struct
        223 * @implements {goog.labs.testing.Matcher}
        224 * @final
        225 */
        226goog.labs.testing.IsUndefinedMatcher = function() {};
        227
        228
        229/**
        230 * Determines if input value is undefined.
        231 *
        232 * @override
        233 */
        234goog.labs.testing.IsUndefinedMatcher.prototype.matches =
        235 function(actualValue) {
        236 return !goog.isDef(actualValue);
        237};
        238
        239
        240/**
        241 * @override
        242 */
        243goog.labs.testing.IsUndefinedMatcher.prototype.describe =
        244 function(actualValue) {
        245 return actualValue + ' is not undefined.';
        246};
        247
        248
        249/**
        250 * Returns a matcher that matches objects that are equal to the input object.
        251 * Equality in this case means the two objects are references to the same
        252 * object.
        253 *
        254 * @param {!Object} object The expected object.
        255 *
        256 * @return {!goog.labs.testing.ObjectEqualsMatcher} A
        257 * ObjectEqualsMatcher.
        258 */
        259function equalsObject(object) {
        260 return new goog.labs.testing.ObjectEqualsMatcher(object);
        261}
        262
        263
        264/**
        265 * Returns a matcher that matches objects that contain the input property.
        266 *
        267 * @param {string} property The property name to check.
        268 *
        269 * @return {!goog.labs.testing.HasPropertyMatcher} A HasPropertyMatcher.
        270 */
        271function hasProperty(property) {
        272 return new goog.labs.testing.HasPropertyMatcher(property);
        273}
        274
        275
        276/**
        277 * Returns a matcher that matches instances of the input class.
        278 *
        279 * @param {!Object} object The class object.
        280 *
        281 * @return {!goog.labs.testing.InstanceOfMatcher} A
        282 * InstanceOfMatcher.
        283 */
        284function instanceOfClass(object) {
        285 return new goog.labs.testing.InstanceOfMatcher(object);
        286}
        287
        288
        289/**
        290 * Returns a matcher that matches all null values.
        291 *
        292 * @return {!goog.labs.testing.IsNullMatcher} A IsNullMatcher.
        293 */
        294function isNull() {
        295 return new goog.labs.testing.IsNullMatcher();
        296}
        297
        298
        299/**
        300 * Returns a matcher that matches all null and undefined values.
        301 *
        302 * @return {!goog.labs.testing.IsNullOrUndefinedMatcher} A
        303 * IsNullOrUndefinedMatcher.
        304 */
        305function isNullOrUndefined() {
        306 return new goog.labs.testing.IsNullOrUndefinedMatcher();
        307}
        308
        309
        310/**
        311 * Returns a matcher that matches undefined values.
        312 *
        313 * @return {!goog.labs.testing.IsUndefinedMatcher} A IsUndefinedMatcher.
        314 */
        315function isUndefined() {
        316 return new goog.labs.testing.IsUndefinedMatcher();
        317}
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/testing/stringmatcher.js.src.html b/docs/source/lib/goog/labs/testing/stringmatcher.js.src.html index 7f7b789..ec37ca4 100644 --- a/docs/source/lib/goog/labs/testing/stringmatcher.js.src.html +++ b/docs/source/lib/goog/labs/testing/stringmatcher.js.src.html @@ -1 +1 @@ -stringmatcher.js

        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
        21goog.provide('goog.labs.testing.ContainsStringMatcher');
        22goog.provide('goog.labs.testing.EndsWithMatcher');
        23goog.provide('goog.labs.testing.EqualToIgnoringCaseMatcher');
        24goog.provide('goog.labs.testing.EqualToIgnoringWhitespaceMatcher');
        25goog.provide('goog.labs.testing.EqualsMatcher');
        26goog.provide('goog.labs.testing.RegexMatcher');
        27goog.provide('goog.labs.testing.StartsWithMatcher');
        28goog.provide('goog.labs.testing.StringContainsInOrderMatcher');
        29
        30
        31goog.require('goog.asserts');
        32goog.require('goog.labs.testing.Matcher');
        33goog.require('goog.string');
        34
        35
        36
        37/**
        38 * The ContainsString matcher.
        39 *
        40 * @param {string} value The expected string.
        41 *
        42 * @constructor
        43 * @implements {goog.labs.testing.Matcher}
        44 */
        45goog.labs.testing.ContainsStringMatcher = function(value) {
        46 /**
        47 * @type {string}
        48 * @private
        49 */
        50 this.value_ = value;
        51};
        52
        53
        54/**
        55 * Determines if input string contains the expected string.
        56 *
        57 * @override
        58 */
        59goog.labs.testing.ContainsStringMatcher.prototype.matches =
        60 function(actualValue) {
        61 goog.asserts.assertString(actualValue);
        62 return goog.string.contains(actualValue, this.value_);
        63};
        64
        65
        66/**
        67 * @override
        68 */
        69goog.labs.testing.ContainsStringMatcher.prototype.describe =
        70 function(actualValue) {
        71 return actualValue + ' does not contain ' + this.value_;
        72};
        73
        74
        75
        76/**
        77 * The EndsWith matcher.
        78 *
        79 * @param {string} value The expected string.
        80 *
        81 * @constructor
        82 * @implements {goog.labs.testing.Matcher}
        83 */
        84goog.labs.testing.EndsWithMatcher = function(value) {
        85 /**
        86 * @type {string}
        87 * @private
        88 */
        89 this.value_ = value;
        90};
        91
        92
        93/**
        94 * Determines if input string ends with the expected string.
        95 *
        96 * @override
        97 */
        98goog.labs.testing.EndsWithMatcher.prototype.matches = function(actualValue) {
        99 goog.asserts.assertString(actualValue);
        100 return goog.string.endsWith(actualValue, this.value_);
        101};
        102
        103
        104/**
        105 * @override
        106 */
        107goog.labs.testing.EndsWithMatcher.prototype.describe =
        108 function(actualValue) {
        109 return actualValue + ' does not end with ' + this.value_;
        110};
        111
        112
        113
        114/**
        115 * The EqualToIgnoringWhitespace matcher.
        116 *
        117 * @param {string} value The expected string.
        118 *
        119 * @constructor
        120 * @implements {goog.labs.testing.Matcher}
        121 */
        122goog.labs.testing.EqualToIgnoringWhitespaceMatcher = function(value) {
        123 /**
        124 * @type {string}
        125 * @private
        126 */
        127 this.value_ = value;
        128};
        129
        130
        131/**
        132 * Determines if input string contains the expected string.
        133 *
        134 * @override
        135 */
        136goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.matches =
        137 function(actualValue) {
        138 goog.asserts.assertString(actualValue);
        139 var string1 = goog.string.collapseWhitespace(actualValue);
        140
        141 return goog.string.caseInsensitiveCompare(this.value_, string1) === 0;
        142};
        143
        144
        145/**
        146 * @override
        147 */
        148goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.describe =
        149 function(actualValue) {
        150 return actualValue + ' is not equal(ignoring whitespace) to ' + this.value_;
        151};
        152
        153
        154
        155/**
        156 * The Equals matcher.
        157 *
        158 * @param {string} value The expected string.
        159 *
        160 * @constructor
        161 * @implements {goog.labs.testing.Matcher}
        162 */
        163goog.labs.testing.EqualsMatcher = function(value) {
        164 /**
        165 * @type {string}
        166 * @private
        167 */
        168 this.value_ = value;
        169};
        170
        171
        172/**
        173 * Determines if input string is equal to the expected string.
        174 *
        175 * @override
        176 */
        177goog.labs.testing.EqualsMatcher.prototype.matches = function(actualValue) {
        178 goog.asserts.assertString(actualValue);
        179 return this.value_ === actualValue;
        180};
        181
        182
        183/**
        184 * @override
        185 */
        186goog.labs.testing.EqualsMatcher.prototype.describe =
        187 function(actualValue) {
        188 return actualValue + ' is not equal to ' + this.value_;
        189};
        190
        191
        192
        193/**
        194 * The MatchesRegex matcher.
        195 *
        196 * @param {!RegExp} regex The expected regex.
        197 *
        198 * @constructor
        199 * @implements {goog.labs.testing.Matcher}
        200 */
        201goog.labs.testing.RegexMatcher = function(regex) {
        202 /**
        203 * @type {!RegExp}
        204 * @private
        205 */
        206 this.regex_ = regex;
        207};
        208
        209
        210/**
        211 * Determines if input string is equal to the expected string.
        212 *
        213 * @override
        214 */
        215goog.labs.testing.RegexMatcher.prototype.matches = function(
        216 actualValue) {
        217 goog.asserts.assertString(actualValue);
        218 return this.regex_.test(actualValue);
        219};
        220
        221
        222/**
        223 * @override
        224 */
        225goog.labs.testing.RegexMatcher.prototype.describe =
        226 function(actualValue) {
        227 return actualValue + ' does not match ' + this.regex_;
        228};
        229
        230
        231
        232/**
        233 * The StartsWith matcher.
        234 *
        235 * @param {string} value The expected string.
        236 *
        237 * @constructor
        238 * @implements {goog.labs.testing.Matcher}
        239 */
        240goog.labs.testing.StartsWithMatcher = function(value) {
        241 /**
        242 * @type {string}
        243 * @private
        244 */
        245 this.value_ = value;
        246};
        247
        248
        249/**
        250 * Determines if input string starts with the expected string.
        251 *
        252 * @override
        253 */
        254goog.labs.testing.StartsWithMatcher.prototype.matches = function(actualValue) {
        255 goog.asserts.assertString(actualValue);
        256 return goog.string.startsWith(actualValue, this.value_);
        257};
        258
        259
        260/**
        261 * @override
        262 */
        263goog.labs.testing.StartsWithMatcher.prototype.describe =
        264 function(actualValue) {
        265 return actualValue + ' does not start with ' + this.value_;
        266};
        267
        268
        269
        270/**
        271 * The StringContainsInOrdermatcher.
        272 *
        273 * @param {Array.<string>} values The expected string values.
        274 *
        275 * @constructor
        276 * @implements {goog.labs.testing.Matcher}
        277 */
        278goog.labs.testing.StringContainsInOrderMatcher = function(values) {
        279 /**
        280 * @type {Array.<string>}
        281 * @private
        282 */
        283 this.values_ = values;
        284};
        285
        286
        287/**
        288 * Determines if input string contains, in order, the expected array of strings.
        289 *
        290 * @override
        291 */
        292goog.labs.testing.StringContainsInOrderMatcher.prototype.matches =
        293 function(actualValue) {
        294 goog.asserts.assertString(actualValue);
        295 var currentIndex, previousIndex = 0;
        296 for (var i = 0; i < this.values_.length; i++) {
        297 currentIndex = goog.string.contains(actualValue, this.values_[i]);
        298 if (currentIndex < 0 || currentIndex < previousIndex) {
        299 return false;
        300 }
        301 previousIndex = currentIndex;
        302 }
        303 return true;
        304};
        305
        306
        307/**
        308 * @override
        309 */
        310goog.labs.testing.StringContainsInOrderMatcher.prototype.describe =
        311 function(actualValue) {
        312 return actualValue + ' does not contain the expected values in order.';
        313};
        314
        315
        316/**
        317 * Matches a string containing the given string.
        318 *
        319 * @param {string} value The expected value.
        320 *
        321 * @return {!goog.labs.testing.ContainsStringMatcher} A
        322 * ContainsStringMatcher.
        323 */
        324function containsString(value) {
        325 return new goog.labs.testing.ContainsStringMatcher(value);
        326}
        327
        328
        329/**
        330 * Matches a string that ends with the given string.
        331 *
        332 * @param {string} value The expected value.
        333 *
        334 * @return {!goog.labs.testing.EndsWithMatcher} A
        335 * EndsWithMatcher.
        336 */
        337function endsWith(value) {
        338 return new goog.labs.testing.EndsWithMatcher(value);
        339}
        340
        341
        342/**
        343 * Matches a string that equals (ignoring whitespace) the given string.
        344 *
        345 * @param {string} value The expected value.
        346 *
        347 * @return {!goog.labs.testing.EqualToIgnoringWhitespaceMatcher} A
        348 * EqualToIgnoringWhitespaceMatcher.
        349 */
        350function equalToIgnoringWhitespace(value) {
        351 return new goog.labs.testing.EqualToIgnoringWhitespaceMatcher(value);
        352}
        353
        354
        355/**
        356 * Matches a string that equals the given string.
        357 *
        358 * @param {string} value The expected value.
        359 *
        360 * @return {!goog.labs.testing.EqualsMatcher} A EqualsMatcher.
        361 */
        362function equals(value) {
        363 return new goog.labs.testing.EqualsMatcher(value);
        364}
        365
        366
        367/**
        368 * Matches a string against a regular expression.
        369 *
        370 * @param {!RegExp} regex The expected regex.
        371 *
        372 * @return {!goog.labs.testing.RegexMatcher} A RegexMatcher.
        373 */
        374function matchesRegex(regex) {
        375 return new goog.labs.testing.RegexMatcher(regex);
        376}
        377
        378
        379/**
        380 * Matches a string that starts with the given string.
        381 *
        382 * @param {string} value The expected value.
        383 *
        384 * @return {!goog.labs.testing.StartsWithMatcher} A
        385 * StartsWithMatcher.
        386 */
        387function startsWith(value) {
        388 return new goog.labs.testing.StartsWithMatcher(value);
        389}
        390
        391
        392/**
        393 * Matches a string that contains the given strings in order.
        394 *
        395 * @param {Array.<string>} values The expected value.
        396 *
        397 * @return {!goog.labs.testing.StringContainsInOrderMatcher} A
        398 * StringContainsInOrderMatcher.
        399 */
        400function stringContainsInOrder(values) {
        401 return new goog.labs.testing.StringContainsInOrderMatcher(values);
        402}
        \ No newline at end of file +stringmatcher.js

        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
        21goog.provide('goog.labs.testing.ContainsStringMatcher');
        22goog.provide('goog.labs.testing.EndsWithMatcher');
        23goog.provide('goog.labs.testing.EqualToIgnoringWhitespaceMatcher');
        24goog.provide('goog.labs.testing.EqualsMatcher');
        25goog.provide('goog.labs.testing.RegexMatcher');
        26goog.provide('goog.labs.testing.StartsWithMatcher');
        27goog.provide('goog.labs.testing.StringContainsInOrderMatcher');
        28
        29
        30goog.require('goog.asserts');
        31goog.require('goog.labs.testing.Matcher');
        32goog.require('goog.string');
        33
        34
        35
        36/**
        37 * The ContainsString matcher.
        38 *
        39 * @param {string} value The expected string.
        40 *
        41 * @constructor
        42 * @struct
        43 * @implements {goog.labs.testing.Matcher}
        44 * @final
        45 */
        46goog.labs.testing.ContainsStringMatcher = function(value) {
        47 /**
        48 * @type {string}
        49 * @private
        50 */
        51 this.value_ = value;
        52};
        53
        54
        55/**
        56 * Determines if input string contains the expected string.
        57 *
        58 * @override
        59 */
        60goog.labs.testing.ContainsStringMatcher.prototype.matches =
        61 function(actualValue) {
        62 goog.asserts.assertString(actualValue);
        63 return goog.string.contains(actualValue, this.value_);
        64};
        65
        66
        67/**
        68 * @override
        69 */
        70goog.labs.testing.ContainsStringMatcher.prototype.describe =
        71 function(actualValue) {
        72 return actualValue + ' does not contain ' + this.value_;
        73};
        74
        75
        76
        77/**
        78 * The EndsWith matcher.
        79 *
        80 * @param {string} value The expected string.
        81 *
        82 * @constructor
        83 * @struct
        84 * @implements {goog.labs.testing.Matcher}
        85 * @final
        86 */
        87goog.labs.testing.EndsWithMatcher = function(value) {
        88 /**
        89 * @type {string}
        90 * @private
        91 */
        92 this.value_ = value;
        93};
        94
        95
        96/**
        97 * Determines if input string ends with the expected string.
        98 *
        99 * @override
        100 */
        101goog.labs.testing.EndsWithMatcher.prototype.matches = function(actualValue) {
        102 goog.asserts.assertString(actualValue);
        103 return goog.string.endsWith(actualValue, this.value_);
        104};
        105
        106
        107/**
        108 * @override
        109 */
        110goog.labs.testing.EndsWithMatcher.prototype.describe =
        111 function(actualValue) {
        112 return actualValue + ' does not end with ' + this.value_;
        113};
        114
        115
        116
        117/**
        118 * The EqualToIgnoringWhitespace matcher.
        119 *
        120 * @param {string} value The expected string.
        121 *
        122 * @constructor
        123 * @struct
        124 * @implements {goog.labs.testing.Matcher}
        125 * @final
        126 */
        127goog.labs.testing.EqualToIgnoringWhitespaceMatcher = function(value) {
        128 /**
        129 * @type {string}
        130 * @private
        131 */
        132 this.value_ = value;
        133};
        134
        135
        136/**
        137 * Determines if input string contains the expected string.
        138 *
        139 * @override
        140 */
        141goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.matches =
        142 function(actualValue) {
        143 goog.asserts.assertString(actualValue);
        144 var string1 = goog.string.collapseWhitespace(actualValue);
        145
        146 return goog.string.caseInsensitiveCompare(this.value_, string1) === 0;
        147};
        148
        149
        150/**
        151 * @override
        152 */
        153goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.describe =
        154 function(actualValue) {
        155 return actualValue + ' is not equal(ignoring whitespace) to ' + this.value_;
        156};
        157
        158
        159
        160/**
        161 * The Equals matcher.
        162 *
        163 * @param {string} value The expected string.
        164 *
        165 * @constructor
        166 * @struct
        167 * @implements {goog.labs.testing.Matcher}
        168 * @final
        169 */
        170goog.labs.testing.EqualsMatcher = function(value) {
        171 /**
        172 * @type {string}
        173 * @private
        174 */
        175 this.value_ = value;
        176};
        177
        178
        179/**
        180 * Determines if input string is equal to the expected string.
        181 *
        182 * @override
        183 */
        184goog.labs.testing.EqualsMatcher.prototype.matches = function(actualValue) {
        185 goog.asserts.assertString(actualValue);
        186 return this.value_ === actualValue;
        187};
        188
        189
        190/**
        191 * @override
        192 */
        193goog.labs.testing.EqualsMatcher.prototype.describe =
        194 function(actualValue) {
        195 return actualValue + ' is not equal to ' + this.value_;
        196};
        197
        198
        199
        200/**
        201 * The MatchesRegex matcher.
        202 *
        203 * @param {!RegExp} regex The expected regex.
        204 *
        205 * @constructor
        206 * @struct
        207 * @implements {goog.labs.testing.Matcher}
        208 * @final
        209 */
        210goog.labs.testing.RegexMatcher = function(regex) {
        211 /**
        212 * @type {!RegExp}
        213 * @private
        214 */
        215 this.regex_ = regex;
        216};
        217
        218
        219/**
        220 * Determines if input string is equal to the expected string.
        221 *
        222 * @override
        223 */
        224goog.labs.testing.RegexMatcher.prototype.matches = function(
        225 actualValue) {
        226 goog.asserts.assertString(actualValue);
        227 return this.regex_.test(actualValue);
        228};
        229
        230
        231/**
        232 * @override
        233 */
        234goog.labs.testing.RegexMatcher.prototype.describe =
        235 function(actualValue) {
        236 return actualValue + ' does not match ' + this.regex_;
        237};
        238
        239
        240
        241/**
        242 * The StartsWith matcher.
        243 *
        244 * @param {string} value The expected string.
        245 *
        246 * @constructor
        247 * @struct
        248 * @implements {goog.labs.testing.Matcher}
        249 * @final
        250 */
        251goog.labs.testing.StartsWithMatcher = function(value) {
        252 /**
        253 * @type {string}
        254 * @private
        255 */
        256 this.value_ = value;
        257};
        258
        259
        260/**
        261 * Determines if input string starts with the expected string.
        262 *
        263 * @override
        264 */
        265goog.labs.testing.StartsWithMatcher.prototype.matches = function(actualValue) {
        266 goog.asserts.assertString(actualValue);
        267 return goog.string.startsWith(actualValue, this.value_);
        268};
        269
        270
        271/**
        272 * @override
        273 */
        274goog.labs.testing.StartsWithMatcher.prototype.describe =
        275 function(actualValue) {
        276 return actualValue + ' does not start with ' + this.value_;
        277};
        278
        279
        280
        281/**
        282 * The StringContainsInOrdermatcher.
        283 *
        284 * @param {Array<string>} values The expected string values.
        285 *
        286 * @constructor
        287 * @struct
        288 * @implements {goog.labs.testing.Matcher}
        289 * @final
        290 */
        291goog.labs.testing.StringContainsInOrderMatcher = function(values) {
        292 /**
        293 * @type {Array<string>}
        294 * @private
        295 */
        296 this.values_ = values;
        297};
        298
        299
        300/**
        301 * Determines if input string contains, in order, the expected array of strings.
        302 *
        303 * @override
        304 */
        305goog.labs.testing.StringContainsInOrderMatcher.prototype.matches =
        306 function(actualValue) {
        307 goog.asserts.assertString(actualValue);
        308 var currentIndex, previousIndex = 0;
        309 for (var i = 0; i < this.values_.length; i++) {
        310 currentIndex = goog.string.contains(actualValue, this.values_[i]);
        311 if (currentIndex < 0 || currentIndex < previousIndex) {
        312 return false;
        313 }
        314 previousIndex = currentIndex;
        315 }
        316 return true;
        317};
        318
        319
        320/**
        321 * @override
        322 */
        323goog.labs.testing.StringContainsInOrderMatcher.prototype.describe =
        324 function(actualValue) {
        325 return actualValue + ' does not contain the expected values in order.';
        326};
        327
        328
        329/**
        330 * Matches a string containing the given string.
        331 *
        332 * @param {string} value The expected value.
        333 *
        334 * @return {!goog.labs.testing.ContainsStringMatcher} A
        335 * ContainsStringMatcher.
        336 */
        337function containsString(value) {
        338 return new goog.labs.testing.ContainsStringMatcher(value);
        339}
        340
        341
        342/**
        343 * Matches a string that ends with the given string.
        344 *
        345 * @param {string} value The expected value.
        346 *
        347 * @return {!goog.labs.testing.EndsWithMatcher} A
        348 * EndsWithMatcher.
        349 */
        350function endsWith(value) {
        351 return new goog.labs.testing.EndsWithMatcher(value);
        352}
        353
        354
        355/**
        356 * Matches a string that equals (ignoring whitespace) the given string.
        357 *
        358 * @param {string} value The expected value.
        359 *
        360 * @return {!goog.labs.testing.EqualToIgnoringWhitespaceMatcher} A
        361 * EqualToIgnoringWhitespaceMatcher.
        362 */
        363function equalToIgnoringWhitespace(value) {
        364 return new goog.labs.testing.EqualToIgnoringWhitespaceMatcher(value);
        365}
        366
        367
        368/**
        369 * Matches a string that equals the given string.
        370 *
        371 * @param {string} value The expected value.
        372 *
        373 * @return {!goog.labs.testing.EqualsMatcher} A EqualsMatcher.
        374 */
        375function equals(value) {
        376 return new goog.labs.testing.EqualsMatcher(value);
        377}
        378
        379
        380/**
        381 * Matches a string against a regular expression.
        382 *
        383 * @param {!RegExp} regex The expected regex.
        384 *
        385 * @return {!goog.labs.testing.RegexMatcher} A RegexMatcher.
        386 */
        387function matchesRegex(regex) {
        388 return new goog.labs.testing.RegexMatcher(regex);
        389}
        390
        391
        392/**
        393 * Matches a string that starts with the given string.
        394 *
        395 * @param {string} value The expected value.
        396 *
        397 * @return {!goog.labs.testing.StartsWithMatcher} A
        398 * StartsWithMatcher.
        399 */
        400function startsWith(value) {
        401 return new goog.labs.testing.StartsWithMatcher(value);
        402}
        403
        404
        405/**
        406 * Matches a string that contains the given strings in order.
        407 *
        408 * @param {Array<string>} values The expected value.
        409 *
        410 * @return {!goog.labs.testing.StringContainsInOrderMatcher} A
        411 * StringContainsInOrderMatcher.
        412 */
        413function stringContainsInOrder(values) {
        414 return new goog.labs.testing.StringContainsInOrderMatcher(values);
        415}
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/useragent/browser.js.src.html b/docs/source/lib/goog/labs/useragent/browser.js.src.html new file mode 100644 index 0000000..0766138 --- /dev/null +++ b/docs/source/lib/goog/labs/useragent/browser.js.src.html @@ -0,0 +1 @@ +browser.js

        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 * @author martone@google.com (Andy Martone)
        23 */
        24
        25goog.provide('goog.labs.userAgent.browser');
        26
        27goog.require('goog.array');
        28goog.require('goog.labs.userAgent.util');
        29goog.require('goog.object');
        30goog.require('goog.string');
        31
        32
        33// TODO(nnaze): Refactor to remove excessive exclusion logic in matching
        34// functions.
        35
        36
        37/**
        38 * @return {boolean} Whether the user's browser is Opera.
        39 * @private
        40 */
        41goog.labs.userAgent.browser.matchOpera_ = function() {
        42 return goog.labs.userAgent.util.matchUserAgent('Opera') ||
        43 goog.labs.userAgent.util.matchUserAgent('OPR');
        44};
        45
        46
        47/**
        48 * @return {boolean} Whether the user's browser is IE.
        49 * @private
        50 */
        51goog.labs.userAgent.browser.matchIE_ = function() {
        52 return goog.labs.userAgent.util.matchUserAgent('Trident') ||
        53 goog.labs.userAgent.util.matchUserAgent('MSIE');
        54};
        55
        56
        57/**
        58 * @return {boolean} Whether the user's browser is Edge.
        59 * @private
        60 */
        61goog.labs.userAgent.browser.matchEdge_ = function() {
        62 return goog.labs.userAgent.util.matchUserAgent('Edge');
        63};
        64
        65
        66/**
        67 * @return {boolean} Whether the user's browser is Firefox.
        68 * @private
        69 */
        70goog.labs.userAgent.browser.matchFirefox_ = function() {
        71 return goog.labs.userAgent.util.matchUserAgent('Firefox');
        72};
        73
        74
        75/**
        76 * @return {boolean} Whether the user's browser is Safari.
        77 * @private
        78 */
        79goog.labs.userAgent.browser.matchSafari_ = function() {
        80 return goog.labs.userAgent.util.matchUserAgent('Safari') &&
        81 !(goog.labs.userAgent.browser.matchChrome_() ||
        82 goog.labs.userAgent.browser.matchCoast_() ||
        83 goog.labs.userAgent.browser.matchOpera_() ||
        84 goog.labs.userAgent.browser.matchEdge_() ||
        85 goog.labs.userAgent.browser.isSilk() ||
        86 goog.labs.userAgent.util.matchUserAgent('Android'));
        87};
        88
        89
        90/**
        91 * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
        92 * iOS browser).
        93 * @private
        94 */
        95goog.labs.userAgent.browser.matchCoast_ = function() {
        96 return goog.labs.userAgent.util.matchUserAgent('Coast');
        97};
        98
        99
        100/**
        101 * @return {boolean} Whether the user's browser is iOS Webview.
        102 * @private
        103 */
        104goog.labs.userAgent.browser.matchIosWebview_ = function() {
        105 // iOS Webview does not show up as Chrome or Safari. Also check for Opera's
        106 // WebKit-based iOS browser, Coast.
        107 return (goog.labs.userAgent.util.matchUserAgent('iPad') ||
        108 goog.labs.userAgent.util.matchUserAgent('iPhone')) &&
        109 !goog.labs.userAgent.browser.matchSafari_() &&
        110 !goog.labs.userAgent.browser.matchChrome_() &&
        111 !goog.labs.userAgent.browser.matchCoast_() &&
        112 goog.labs.userAgent.util.matchUserAgent('AppleWebKit');
        113};
        114
        115
        116/**
        117 * @return {boolean} Whether the user's browser is Chrome.
        118 * @private
        119 */
        120goog.labs.userAgent.browser.matchChrome_ = function() {
        121 return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||
        122 goog.labs.userAgent.util.matchUserAgent('CriOS')) &&
        123 !goog.labs.userAgent.browser.matchOpera_() &&
        124 !goog.labs.userAgent.browser.matchEdge_();
        125};
        126
        127
        128/**
        129 * @return {boolean} Whether the user's browser is the Android browser.
        130 * @private
        131 */
        132goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
        133 // Android can appear in the user agent string for Chrome on Android.
        134 // This is not the Android standalone browser if it does.
        135 return goog.labs.userAgent.util.matchUserAgent('Android') &&
        136 !(goog.labs.userAgent.browser.isChrome() ||
        137 goog.labs.userAgent.browser.isFirefox() ||
        138 goog.labs.userAgent.browser.isOpera() ||
        139 goog.labs.userAgent.browser.isSilk());
        140};
        141
        142
        143/**
        144 * @return {boolean} Whether the user's browser is Opera.
        145 */
        146goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;
        147
        148
        149/**
        150 * @return {boolean} Whether the user's browser is IE.
        151 */
        152goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;
        153
        154
        155/**
        156 * @return {boolean} Whether the user's browser is Edge.
        157 */
        158goog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdge_;
        159
        160
        161/**
        162 * @return {boolean} Whether the user's browser is Firefox.
        163 */
        164goog.labs.userAgent.browser.isFirefox =
        165 goog.labs.userAgent.browser.matchFirefox_;
        166
        167
        168/**
        169 * @return {boolean} Whether the user's browser is Safari.
        170 */
        171goog.labs.userAgent.browser.isSafari =
        172 goog.labs.userAgent.browser.matchSafari_;
        173
        174
        175/**
        176 * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
        177 * iOS browser).
        178 */
        179goog.labs.userAgent.browser.isCoast =
        180 goog.labs.userAgent.browser.matchCoast_;
        181
        182
        183/**
        184 * @return {boolean} Whether the user's browser is iOS Webview.
        185 */
        186goog.labs.userAgent.browser.isIosWebview =
        187 goog.labs.userAgent.browser.matchIosWebview_;
        188
        189
        190/**
        191 * @return {boolean} Whether the user's browser is Chrome.
        192 */
        193goog.labs.userAgent.browser.isChrome =
        194 goog.labs.userAgent.browser.matchChrome_;
        195
        196
        197/**
        198 * @return {boolean} Whether the user's browser is the Android browser.
        199 */
        200goog.labs.userAgent.browser.isAndroidBrowser =
        201 goog.labs.userAgent.browser.matchAndroidBrowser_;
        202
        203
        204/**
        205 * For more information, see:
        206 * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
        207 * @return {boolean} Whether the user's browser is Silk.
        208 */
        209goog.labs.userAgent.browser.isSilk = function() {
        210 return goog.labs.userAgent.util.matchUserAgent('Silk');
        211};
        212
        213
        214/**
        215 * @return {string} The browser version or empty string if version cannot be
        216 * determined. Note that for Internet Explorer, this returns the version of
        217 * the browser, not the version of the rendering engine. (IE 8 in
        218 * compatibility mode will return 8.0 rather than 7.0. To determine the
        219 * rendering engine version, look at document.documentMode instead. See
        220 * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
        221 * details.)
        222 */
        223goog.labs.userAgent.browser.getVersion = function() {
        224 var userAgentString = goog.labs.userAgent.util.getUserAgent();
        225 // Special case IE since IE's version is inside the parenthesis and
        226 // without the '/'.
        227 if (goog.labs.userAgent.browser.isIE()) {
        228 return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
        229 }
        230
        231 var versionTuples = goog.labs.userAgent.util.extractVersionTuples(
        232 userAgentString);
        233
        234 // Construct a map for easy lookup.
        235 var versionMap = {};
        236 goog.array.forEach(versionTuples, function(tuple) {
        237 // Note that the tuple is of length three, but we only care about the
        238 // first two.
        239 var key = tuple[0];
        240 var value = tuple[1];
        241 versionMap[key] = value;
        242 });
        243
        244 var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);
        245
        246 // Gives the value with the first key it finds, otherwise empty string.
        247 function lookUpValueWithKeys(keys) {
        248 var key = goog.array.find(keys, versionMapHasKey);
        249 return versionMap[key] || '';
        250 }
        251
        252 // Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
        253 // See
        254 // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
        255 if (goog.labs.userAgent.browser.isOpera()) {
        256 // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
        257 // Opera uses 'OPR' for more recent UAs.
        258 return lookUpValueWithKeys(['Version', 'Opera', 'OPR']);
        259 }
        260
        261 // Check Edge before Chrome since it has Chrome in the string.
        262 if (goog.labs.userAgent.browser.isEdge()) {
        263 return lookUpValueWithKeys(['Edge']);
        264 }
        265
        266 if (goog.labs.userAgent.browser.isChrome()) {
        267 return lookUpValueWithKeys(['Chrome', 'CriOS']);
        268 }
        269
        270 // Usually products browser versions are in the third tuple after "Mozilla"
        271 // and the engine.
        272 var tuple = versionTuples[2];
        273 return tuple && tuple[1] || '';
        274};
        275
        276
        277/**
        278 * @param {string|number} version The version to check.
        279 * @return {boolean} Whether the browser version is higher or the same as the
        280 * given version.
        281 */
        282goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
        283 return goog.string.compareVersions(goog.labs.userAgent.browser.getVersion(),
        284 version) >= 0;
        285};
        286
        287
        288/**
        289 * Determines IE version. More information:
        290 * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
        291 * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
        292 * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
        293 * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
        294 *
        295 * @param {string} userAgent the User-Agent.
        296 * @return {string}
        297 * @private
        298 */
        299goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
        300 // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
        301 // bug. Example UA:
        302 // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
        303 // like Gecko.
        304 // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
        305 var rv = /rv: *([\d\.]*)/.exec(userAgent);
        306 if (rv && rv[1]) {
        307 return rv[1];
        308 }
        309
        310 var version = '';
        311 var msie = /MSIE +([\d\.]+)/.exec(userAgent);
        312 if (msie && msie[1]) {
        313 // IE in compatibility mode usually identifies itself as MSIE 7.0; in this
        314 // case, use the Trident version to determine the version of IE. For more
        315 // details, see the links above.
        316 var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
        317 if (msie[1] == '7.0') {
        318 if (tridentVersion && tridentVersion[1]) {
        319 switch (tridentVersion[1]) {
        320 case '4.0':
        321 version = '8.0';
        322 break;
        323 case '5.0':
        324 version = '9.0';
        325 break;
        326 case '6.0':
        327 version = '10.0';
        328 break;
        329 case '7.0':
        330 version = '11.0';
        331 break;
        332 }
        333 } else {
        334 version = '7.0';
        335 }
        336 } else {
        337 version = msie[1];
        338 }
        339 }
        340 return version;
        341};
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/useragent/engine.js.src.html b/docs/source/lib/goog/labs/useragent/engine.js.src.html new file mode 100644 index 0000000..8b32be2 --- /dev/null +++ b/docs/source/lib/goog/labs/useragent/engine.js.src.html @@ -0,0 +1 @@ +engine.js

        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
        23goog.provide('goog.labs.userAgent.engine');
        24
        25goog.require('goog.array');
        26goog.require('goog.labs.userAgent.util');
        27goog.require('goog.string');
        28
        29
        30/**
        31 * @return {boolean} Whether the rendering engine is Presto.
        32 */
        33goog.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 */
        41goog.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 Edge.
        50 */
        51goog.labs.userAgent.engine.isEdge = function() {
        52 return goog.labs.userAgent.util.matchUserAgent('Edge');
        53};
        54
        55
        56/**
        57 * @return {boolean} Whether the rendering engine is WebKit.
        58 */
        59goog.labs.userAgent.engine.isWebKit = function() {
        60 return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit') &&
        61 !goog.labs.userAgent.engine.isEdge();
        62};
        63
        64
        65/**
        66 * @return {boolean} Whether the rendering engine is Gecko.
        67 */
        68goog.labs.userAgent.engine.isGecko = function() {
        69 return goog.labs.userAgent.util.matchUserAgent('Gecko') &&
        70 !goog.labs.userAgent.engine.isWebKit() &&
        71 !goog.labs.userAgent.engine.isTrident() &&
        72 !goog.labs.userAgent.engine.isEdge();
        73};
        74
        75
        76/**
        77 * @return {string} The rendering engine's version or empty string if version
        78 * can't be determined.
        79 */
        80goog.labs.userAgent.engine.getVersion = function() {
        81 var userAgentString = goog.labs.userAgent.util.getUserAgent();
        82 if (userAgentString) {
        83 var tuples = goog.labs.userAgent.util.extractVersionTuples(
        84 userAgentString);
        85
        86 var engineTuple = goog.labs.userAgent.engine.getEngineTuple_(tuples);
        87 if (engineTuple) {
        88 // In Gecko, the version string is either in the browser info or the
        89 // Firefox version. See Gecko user agent string reference:
        90 // http://goo.gl/mULqa
        91 if (engineTuple[0] == 'Gecko') {
        92 return goog.labs.userAgent.engine.getVersionForKey_(
        93 tuples, 'Firefox');
        94 }
        95
        96 return engineTuple[1];
        97 }
        98
        99 // MSIE has only one version identifier, and the Trident version is
        100 // specified in the parenthetical. IE Edge is covered in the engine tuple
        101 // detection.
        102 var browserTuple = tuples[0];
        103 var info;
        104 if (browserTuple && (info = browserTuple[2])) {
        105 var match = /Trident\/([^\s;]+)/.exec(info);
        106 if (match) {
        107 return match[1];
        108 }
        109 }
        110 }
        111 return '';
        112};
        113
        114
        115/**
        116 * @param {!Array<!Array<string>>} tuples Extracted version tuples.
        117 * @return {!Array<string>|undefined} The engine tuple or undefined if not
        118 * found.
        119 * @private
        120 */
        121goog.labs.userAgent.engine.getEngineTuple_ = function(tuples) {
        122 if (!goog.labs.userAgent.engine.isEdge()) {
        123 return tuples[1];
        124 }
        125 for (var i = 0; i < tuples.length; i++) {
        126 var tuple = tuples[i];
        127 if (tuple[0] == 'Edge') {
        128 return tuple;
        129 }
        130 }
        131};
        132
        133
        134/**
        135 * @param {string|number} version The version to check.
        136 * @return {boolean} Whether the rendering engine version is higher or the same
        137 * as the given version.
        138 */
        139goog.labs.userAgent.engine.isVersionOrHigher = function(version) {
        140 return goog.string.compareVersions(goog.labs.userAgent.engine.getVersion(),
        141 version) >= 0;
        142};
        143
        144
        145/**
        146 * @param {!Array<!Array<string>>} tuples Version tuples.
        147 * @param {string} key The key to look for.
        148 * @return {string} The version string of the given key, if present.
        149 * Otherwise, the empty string.
        150 * @private
        151 */
        152goog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) {
        153 // TODO(nnaze): Move to util if useful elsewhere.
        154
        155 var pair = goog.array.find(tuples, function(pair) {
        156 return key == pair[0];
        157 });
        158
        159 return pair && pair[1] || '';
        160};
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/useragent/platform.js.src.html b/docs/source/lib/goog/labs/useragent/platform.js.src.html new file mode 100644 index 0000000..f141003 --- /dev/null +++ b/docs/source/lib/goog/labs/useragent/platform.js.src.html @@ -0,0 +1 @@ +platform.js

        lib/goog/labs/useragent/platform.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 platform detection.
        17 * @see <a href="http://www.useragentstring.com/">User agent strings</a>
        18 * For more information on browser brand, rendering engine, or device see the
        19 * other sub-namespaces in goog.labs.userAgent (browser, engine, and device
        20 * respectively).
        21 *
        22 */
        23
        24goog.provide('goog.labs.userAgent.platform');
        25
        26goog.require('goog.labs.userAgent.util');
        27goog.require('goog.string');
        28
        29
        30/**
        31 * @return {boolean} Whether the platform is Android.
        32 */
        33goog.labs.userAgent.platform.isAndroid = function() {
        34 return goog.labs.userAgent.util.matchUserAgent('Android');
        35};
        36
        37
        38/**
        39 * @return {boolean} Whether the platform is iPod.
        40 */
        41goog.labs.userAgent.platform.isIpod = function() {
        42 return goog.labs.userAgent.util.matchUserAgent('iPod');
        43};
        44
        45
        46/**
        47 * @return {boolean} Whether the platform is iPhone.
        48 */
        49goog.labs.userAgent.platform.isIphone = function() {
        50 return goog.labs.userAgent.util.matchUserAgent('iPhone') &&
        51 !goog.labs.userAgent.util.matchUserAgent('iPod') &&
        52 !goog.labs.userAgent.util.matchUserAgent('iPad');
        53};
        54
        55
        56/**
        57 * @return {boolean} Whether the platform is iPad.
        58 */
        59goog.labs.userAgent.platform.isIpad = function() {
        60 return goog.labs.userAgent.util.matchUserAgent('iPad');
        61};
        62
        63
        64/**
        65 * @return {boolean} Whether the platform is iOS.
        66 */
        67goog.labs.userAgent.platform.isIos = function() {
        68 return goog.labs.userAgent.platform.isIphone() ||
        69 goog.labs.userAgent.platform.isIpad() ||
        70 goog.labs.userAgent.platform.isIpod();
        71};
        72
        73
        74/**
        75 * @return {boolean} Whether the platform is Mac.
        76 */
        77goog.labs.userAgent.platform.isMacintosh = function() {
        78 return goog.labs.userAgent.util.matchUserAgent('Macintosh');
        79};
        80
        81
        82/**
        83 * Note: ChromeOS is not considered to be Linux as it does not report itself
        84 * as Linux in the user agent string.
        85 * @return {boolean} Whether the platform is Linux.
        86 */
        87goog.labs.userAgent.platform.isLinux = function() {
        88 return goog.labs.userAgent.util.matchUserAgent('Linux');
        89};
        90
        91
        92/**
        93 * @return {boolean} Whether the platform is Windows.
        94 */
        95goog.labs.userAgent.platform.isWindows = function() {
        96 return goog.labs.userAgent.util.matchUserAgent('Windows');
        97};
        98
        99
        100/**
        101 * @return {boolean} Whether the platform is ChromeOS.
        102 */
        103goog.labs.userAgent.platform.isChromeOS = function() {
        104 return goog.labs.userAgent.util.matchUserAgent('CrOS');
        105};
        106
        107
        108/**
        109 * The version of the platform. We only determine the version for Windows,
        110 * Mac, and Chrome OS. It doesn't make much sense on Linux. For Windows, we only
        111 * look at the NT version. Non-NT-based versions (e.g. 95, 98, etc.) are given
        112 * version 0.0.
        113 *
        114 * @return {string} The platform version or empty string if version cannot be
        115 * determined.
        116 */
        117goog.labs.userAgent.platform.getVersion = function() {
        118 var userAgentString = goog.labs.userAgent.util.getUserAgent();
        119 var version = '', re;
        120 if (goog.labs.userAgent.platform.isWindows()) {
        121 re = /Windows (?:NT|Phone) ([0-9.]+)/;
        122 var match = re.exec(userAgentString);
        123 if (match) {
        124 version = match[1];
        125 } else {
        126 version = '0.0';
        127 }
        128 } else if (goog.labs.userAgent.platform.isIos()) {
        129 re = /(?:iPhone|iPod|iPad|CPU)\s+OS\s+(\S+)/;
        130 var match = re.exec(userAgentString);
        131 // Report the version as x.y.z and not x_y_z
        132 version = match && match[1].replace(/_/g, '.');
        133 } else if (goog.labs.userAgent.platform.isMacintosh()) {
        134 re = /Mac OS X ([0-9_.]+)/;
        135 var match = re.exec(userAgentString);
        136 // Note: some old versions of Camino do not report an OSX version.
        137 // Default to 10.
        138 version = match ? match[1].replace(/_/g, '.') : '10';
        139 } else if (goog.labs.userAgent.platform.isAndroid()) {
        140 re = /Android\s+([^\);]+)(\)|;)/;
        141 var match = re.exec(userAgentString);
        142 version = match && match[1];
        143 } else if (goog.labs.userAgent.platform.isChromeOS()) {
        144 re = /(?:CrOS\s+(?:i686|x86_64)\s+([0-9.]+))/;
        145 var match = re.exec(userAgentString);
        146 version = match && match[1];
        147 }
        148 return version || '';
        149};
        150
        151
        152/**
        153 * @param {string|number} version The version to check.
        154 * @return {boolean} Whether the browser version is higher or the same as the
        155 * given version.
        156 */
        157goog.labs.userAgent.platform.isVersionOrHigher = function(version) {
        158 return goog.string.compareVersions(goog.labs.userAgent.platform.getVersion(),
        159 version) >= 0;
        160};
        \ No newline at end of file diff --git a/docs/source/lib/goog/labs/useragent/util.js.src.html b/docs/source/lib/goog/labs/useragent/util.js.src.html new file mode 100644 index 0000000..6f4e40e --- /dev/null +++ b/docs/source/lib/goog/labs/useragent/util.js.src.html @@ -0,0 +1 @@ +util.js

        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 *
        20 * @author nnaze@google.com (Nathan Naze)
        21 */
        22
        23goog.provide('goog.labs.userAgent.util');
        24
        25goog.require('goog.string');
        26
        27
        28/**
        29 * Gets the native userAgent string from navigator if it exists.
        30 * If navigator or navigator.userAgent string is missing, returns an empty
        31 * string.
        32 * @return {string}
        33 * @private
        34 */
        35goog.labs.userAgent.util.getNativeUserAgentString_ = function() {
        36 var navigator = goog.labs.userAgent.util.getNavigator_();
        37 if (navigator) {
        38 var userAgent = navigator.userAgent;
        39 if (userAgent) {
        40 return userAgent;
        41 }
        42 }
        43 return '';
        44};
        45
        46
        47/**
        48 * Getter for the native navigator.
        49 * This is a separate function so it can be stubbed out in testing.
        50 * @return {Navigator}
        51 * @private
        52 */
        53goog.labs.userAgent.util.getNavigator_ = function() {
        54 return goog.global.navigator;
        55};
        56
        57
        58/**
        59 * A possible override for applications which wish to not check
        60 * navigator.userAgent but use a specified value for detection instead.
        61 * @private {string}
        62 */
        63goog.labs.userAgent.util.userAgent_ =
        64 goog.labs.userAgent.util.getNativeUserAgentString_();
        65
        66
        67/**
        68 * Applications may override browser detection on the built in
        69 * navigator.userAgent object by setting this string. Set to null to use the
        70 * browser object instead.
        71 * @param {?string=} opt_userAgent The User-Agent override.
        72 */
        73goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {
        74 goog.labs.userAgent.util.userAgent_ = opt_userAgent ||
        75 goog.labs.userAgent.util.getNativeUserAgentString_();
        76};
        77
        78
        79/**
        80 * @return {string} The user agent string.
        81 */
        82goog.labs.userAgent.util.getUserAgent = function() {
        83 return goog.labs.userAgent.util.userAgent_;
        84};
        85
        86
        87/**
        88 * @param {string} str
        89 * @return {boolean} Whether the user agent contains the given string, ignoring
        90 * case.
        91 */
        92goog.labs.userAgent.util.matchUserAgent = function(str) {
        93 var userAgent = goog.labs.userAgent.util.getUserAgent();
        94 return goog.string.contains(userAgent, str);
        95};
        96
        97
        98/**
        99 * @param {string} str
        100 * @return {boolean} Whether the user agent contains the given string.
        101 */
        102goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {
        103 var userAgent = goog.labs.userAgent.util.getUserAgent();
        104 return goog.string.caseInsensitiveContains(userAgent, str);
        105};
        106
        107
        108/**
        109 * Parses the user agent into tuples for each section.
        110 * @param {string} userAgent
        111 * @return {!Array<!Array<string>>} Tuples of key, version, and the contents
        112 * of the parenthetical.
        113 */
        114goog.labs.userAgent.util.extractVersionTuples = function(userAgent) {
        115 // Matches each section of a user agent string.
        116 // Example UA:
        117 // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)
        118 // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405
        119 // This has three version tuples: Mozilla, AppleWebKit, and Mobile.
        120
        121 var versionRegExp = new RegExp(
        122 // Key. Note that a key may have a space.
        123 // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')
        124 '(\\w[\\w ]+)' +
        125
        126 '/' + // slash
        127 '([^\\s]+)' + // version (i.e. '5.0b')
        128 '\\s*' + // whitespace
        129 '(?:\\((.*?)\\))?', // parenthetical info. parentheses not matched.
        130 'g');
        131
        132 var data = [];
        133 var match;
        134
        135 // Iterate and collect the version tuples. Each iteration will be the
        136 // next regex match.
        137 while (match = versionRegExp.exec(userAgent)) {
        138 data.push([
        139 match[1], // key
        140 match[2], // value
        141 // || undefined as this is not undefined in IE7 and IE8
        142 match[3] || undefined // info
        143 ]);
        144 }
        145
        146 return data;
        147};
        148
        \ No newline at end of file diff --git a/docs/source/lib/goog/log/log.js.src.html b/docs/source/lib/goog/log/log.js.src.html new file mode 100644 index 0000000..a0bae37 --- /dev/null +++ b/docs/source/lib/goog/log/log.js.src.html @@ -0,0 +1 @@ +log.js

        lib/goog/log/log.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 Basic strippable logging definitions.
        17 * @see http://go/closurelogging
        18 *
        19 * @author johnlenz@google.com (John Lenz)
        20 */
        21
        22goog.provide('goog.log');
        23goog.provide('goog.log.Level');
        24goog.provide('goog.log.LogRecord');
        25goog.provide('goog.log.Logger');
        26
        27goog.require('goog.debug');
        28goog.require('goog.debug.LogManager');
        29goog.require('goog.debug.LogRecord');
        30goog.require('goog.debug.Logger');
        31
        32
        33/** @define {boolean} Whether logging is enabled. */
        34goog.define('goog.log.ENABLED', goog.debug.LOGGING_ENABLED);
        35
        36
        37/** @const */
        38goog.log.ROOT_LOGGER_NAME = goog.debug.Logger.ROOT_LOGGER_NAME;
        39
        40
        41
        42/**
        43 * @constructor
        44 * @final
        45 */
        46goog.log.Logger = goog.debug.Logger;
        47
        48
        49
        50/**
        51 * @constructor
        52 * @final
        53 */
        54goog.log.Level = goog.debug.Logger.Level;
        55
        56
        57
        58/**
        59 * @constructor
        60 * @final
        61 */
        62goog.log.LogRecord = goog.debug.LogRecord;
        63
        64
        65/**
        66 * Finds or creates a logger for a named subsystem. If a logger has already been
        67 * created with the given name it is returned. Otherwise a new logger is
        68 * created. If a new logger is created its log level will be configured based
        69 * on the goog.debug.LogManager configuration and it will configured to also
        70 * send logging output to its parent's handlers.
        71 * @see goog.debug.LogManager
        72 *
        73 * @param {string} name A name for the logger. This should be a dot-separated
        74 * name and should normally be based on the package name or class name of
        75 * the subsystem, such as goog.net.BrowserChannel.
        76 * @param {goog.log.Level=} opt_level If provided, override the
        77 * default logging level with the provided level.
        78 * @return {goog.log.Logger} The named logger or null if logging is disabled.
        79 */
        80goog.log.getLogger = function(name, opt_level) {
        81 if (goog.log.ENABLED) {
        82 var logger = goog.debug.LogManager.getLogger(name);
        83 if (opt_level && logger) {
        84 logger.setLevel(opt_level);
        85 }
        86 return logger;
        87 } else {
        88 return null;
        89 }
        90};
        91
        92
        93// TODO(johnlenz): try to tighten the types to these functions.
        94/**
        95 * Adds a handler to the logger. This doesn't use the event system because
        96 * we want to be able to add logging to the event system.
        97 * @param {goog.log.Logger} logger
        98 * @param {Function} handler Handler function to add.
        99 */
        100goog.log.addHandler = function(logger, handler) {
        101 if (goog.log.ENABLED && logger) {
        102 logger.addHandler(handler);
        103 }
        104};
        105
        106
        107/**
        108 * Removes a handler from the logger. This doesn't use the event system because
        109 * we want to be able to add logging to the event system.
        110 * @param {goog.log.Logger} logger
        111 * @param {Function} handler Handler function to remove.
        112 * @return {boolean} Whether the handler was removed.
        113 */
        114goog.log.removeHandler = function(logger, handler) {
        115 if (goog.log.ENABLED && logger) {
        116 return logger.removeHandler(handler);
        117 } else {
        118 return false;
        119 }
        120};
        121
        122
        123/**
        124 * Logs a message. If the logger is currently enabled for the
        125 * given message level then the given message is forwarded to all the
        126 * registered output Handler objects.
        127 * @param {goog.log.Logger} logger
        128 * @param {goog.log.Level} level One of the level identifiers.
        129 * @param {goog.debug.Loggable} msg The message to log.
        130 * @param {Error|Object=} opt_exception An exception associated with the
        131 * message.
        132 */
        133goog.log.log = function(logger, level, msg, opt_exception) {
        134 if (goog.log.ENABLED && logger) {
        135 logger.log(level, msg, opt_exception);
        136 }
        137};
        138
        139
        140/**
        141 * Logs a message at the Level.SEVERE level.
        142 * If the logger is currently enabled for the given message level then the
        143 * given message is forwarded to all the registered output Handler objects.
        144 * @param {goog.log.Logger} logger
        145 * @param {goog.debug.Loggable} msg The message to log.
        146 * @param {Error=} opt_exception An exception associated with the message.
        147 */
        148goog.log.error = function(logger, msg, opt_exception) {
        149 if (goog.log.ENABLED && logger) {
        150 logger.severe(msg, opt_exception);
        151 }
        152};
        153
        154
        155/**
        156 * Logs a message at the Level.WARNING level.
        157 * If the logger is currently enabled for the given message level then the
        158 * given message is forwarded to all the registered output Handler objects.
        159 * @param {goog.log.Logger} logger
        160 * @param {goog.debug.Loggable} msg The message to log.
        161 * @param {Error=} opt_exception An exception associated with the message.
        162 */
        163goog.log.warning = function(logger, msg, opt_exception) {
        164 if (goog.log.ENABLED && logger) {
        165 logger.warning(msg, opt_exception);
        166 }
        167};
        168
        169
        170/**
        171 * Logs a message at the Level.INFO level.
        172 * If the logger is currently enabled for the given message level then the
        173 * given message is forwarded to all the registered output Handler objects.
        174 * @param {goog.log.Logger} logger
        175 * @param {goog.debug.Loggable} msg The message to log.
        176 * @param {Error=} opt_exception An exception associated with the message.
        177 */
        178goog.log.info = function(logger, msg, opt_exception) {
        179 if (goog.log.ENABLED && logger) {
        180 logger.info(msg, opt_exception);
        181 }
        182};
        183
        184
        185/**
        186 * Logs a message at the Level.Fine level.
        187 * If the logger is currently enabled for the given message level then the
        188 * given message is forwarded to all the registered output Handler objects.
        189 * @param {goog.log.Logger} logger
        190 * @param {goog.debug.Loggable} msg The message to log.
        191 * @param {Error=} opt_exception An exception associated with the message.
        192 */
        193goog.log.fine = function(logger, msg, opt_exception) {
        194 if (goog.log.ENABLED && logger) {
        195 logger.fine(msg, opt_exception);
        196 }
        197};
        \ No newline at end of file diff --git a/docs/source/lib/goog/math/box.js.src.html b/docs/source/lib/goog/math/box.js.src.html new file mode 100644 index 0000000..7d2bd93 --- /dev/null +++ b/docs/source/lib/goog/math/box.js.src.html @@ -0,0 +1 @@ +box.js

        lib/goog/math/box.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 A utility class for representing a numeric box.
        17 */
        18
        19
        20goog.provide('goog.math.Box');
        21
        22goog.require('goog.math.Coordinate');
        23
        24
        25
        26/**
        27 * Class for representing a box. A box is specified as a top, right, bottom,
        28 * and left. A box is useful for representing margins and padding.
        29 *
        30 * This class assumes 'screen coordinates': larger Y coordinates are further
        31 * from the top of the screen.
        32 *
        33 * @param {number} top Top.
        34 * @param {number} right Right.
        35 * @param {number} bottom Bottom.
        36 * @param {number} left Left.
        37 * @struct
        38 * @constructor
        39 */
        40goog.math.Box = function(top, right, bottom, left) {
        41 /**
        42 * Top
        43 * @type {number}
        44 */
        45 this.top = top;
        46
        47 /**
        48 * Right
        49 * @type {number}
        50 */
        51 this.right = right;
        52
        53 /**
        54 * Bottom
        55 * @type {number}
        56 */
        57 this.bottom = bottom;
        58
        59 /**
        60 * Left
        61 * @type {number}
        62 */
        63 this.left = left;
        64};
        65
        66
        67/**
        68 * Creates a Box by bounding a collection of goog.math.Coordinate objects
        69 * @param {...goog.math.Coordinate} var_args Coordinates to be included inside
        70 * the box.
        71 * @return {!goog.math.Box} A Box containing all the specified Coordinates.
        72 */
        73goog.math.Box.boundingBox = function(var_args) {
        74 var box = new goog.math.Box(arguments[0].y, arguments[0].x,
        75 arguments[0].y, arguments[0].x);
        76 for (var i = 1; i < arguments.length; i++) {
        77 box.expandToIncludeCoordinate(arguments[i]);
        78 }
        79 return box;
        80};
        81
        82
        83/**
        84 * @return {number} width The width of this Box.
        85 */
        86goog.math.Box.prototype.getWidth = function() {
        87 return this.right - this.left;
        88};
        89
        90
        91/**
        92 * @return {number} height The height of this Box.
        93 */
        94goog.math.Box.prototype.getHeight = function() {
        95 return this.bottom - this.top;
        96};
        97
        98
        99/**
        100 * Creates a copy of the box with the same dimensions.
        101 * @return {!goog.math.Box} A clone of this Box.
        102 */
        103goog.math.Box.prototype.clone = function() {
        104 return new goog.math.Box(this.top, this.right, this.bottom, this.left);
        105};
        106
        107
        108if (goog.DEBUG) {
        109 /**
        110 * Returns a nice string representing the box.
        111 * @return {string} In the form (50t, 73r, 24b, 13l).
        112 * @override
        113 */
        114 goog.math.Box.prototype.toString = function() {
        115 return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
        116 this.left + 'l)';
        117 };
        118}
        119
        120
        121/**
        122 * Returns whether the box contains a coordinate or another box.
        123 *
        124 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
        125 * @return {boolean} Whether the box contains the coordinate or other box.
        126 */
        127goog.math.Box.prototype.contains = function(other) {
        128 return goog.math.Box.contains(this, other);
        129};
        130
        131
        132/**
        133 * Expands box with the given margins.
        134 *
        135 * @param {number|goog.math.Box} top Top margin or box with all margins.
        136 * @param {number=} opt_right Right margin.
        137 * @param {number=} opt_bottom Bottom margin.
        138 * @param {number=} opt_left Left margin.
        139 * @return {!goog.math.Box} A reference to this Box.
        140 */
        141goog.math.Box.prototype.expand = function(top, opt_right, opt_bottom,
        142 opt_left) {
        143 if (goog.isObject(top)) {
        144 this.top -= top.top;
        145 this.right += top.right;
        146 this.bottom += top.bottom;
        147 this.left -= top.left;
        148 } else {
        149 this.top -= top;
        150 this.right += opt_right;
        151 this.bottom += opt_bottom;
        152 this.left -= opt_left;
        153 }
        154
        155 return this;
        156};
        157
        158
        159/**
        160 * Expand this box to include another box.
        161 * NOTE(user): This is used in code that needs to be very fast, please don't
        162 * add functionality to this function at the expense of speed (variable
        163 * arguments, accepting multiple argument types, etc).
        164 * @param {goog.math.Box} box The box to include in this one.
        165 */
        166goog.math.Box.prototype.expandToInclude = function(box) {
        167 this.left = Math.min(this.left, box.left);
        168 this.top = Math.min(this.top, box.top);
        169 this.right = Math.max(this.right, box.right);
        170 this.bottom = Math.max(this.bottom, box.bottom);
        171};
        172
        173
        174/**
        175 * Expand this box to include the coordinate.
        176 * @param {!goog.math.Coordinate} coord The coordinate to be included
        177 * inside the box.
        178 */
        179goog.math.Box.prototype.expandToIncludeCoordinate = function(coord) {
        180 this.top = Math.min(this.top, coord.y);
        181 this.right = Math.max(this.right, coord.x);
        182 this.bottom = Math.max(this.bottom, coord.y);
        183 this.left = Math.min(this.left, coord.x);
        184};
        185
        186
        187/**
        188 * Compares boxes for equality.
        189 * @param {goog.math.Box} a A Box.
        190 * @param {goog.math.Box} b A Box.
        191 * @return {boolean} True iff the boxes are equal, or if both are null.
        192 */
        193goog.math.Box.equals = function(a, b) {
        194 if (a == b) {
        195 return true;
        196 }
        197 if (!a || !b) {
        198 return false;
        199 }
        200 return a.top == b.top && a.right == b.right &&
        201 a.bottom == b.bottom && a.left == b.left;
        202};
        203
        204
        205/**
        206 * Returns whether a box contains a coordinate or another box.
        207 *
        208 * @param {goog.math.Box} box A Box.
        209 * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
        210 * @return {boolean} Whether the box contains the coordinate or other box.
        211 */
        212goog.math.Box.contains = function(box, other) {
        213 if (!box || !other) {
        214 return false;
        215 }
        216
        217 if (other instanceof goog.math.Box) {
        218 return other.left >= box.left && other.right <= box.right &&
        219 other.top >= box.top && other.bottom <= box.bottom;
        220 }
        221
        222 // other is a Coordinate.
        223 return other.x >= box.left && other.x <= box.right &&
        224 other.y >= box.top && other.y <= box.bottom;
        225};
        226
        227
        228/**
        229 * Returns the relative x position of a coordinate compared to a box. Returns
        230 * zero if the coordinate is inside the box.
        231 *
        232 * @param {goog.math.Box} box A Box.
        233 * @param {goog.math.Coordinate} coord A Coordinate.
        234 * @return {number} The x position of {@code coord} relative to the nearest
        235 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
        236 */
        237goog.math.Box.relativePositionX = function(box, coord) {
        238 if (coord.x < box.left) {
        239 return coord.x - box.left;
        240 } else if (coord.x > box.right) {
        241 return coord.x - box.right;
        242 }
        243 return 0;
        244};
        245
        246
        247/**
        248 * Returns the relative y position of a coordinate compared to a box. Returns
        249 * zero if the coordinate is inside the box.
        250 *
        251 * @param {goog.math.Box} box A Box.
        252 * @param {goog.math.Coordinate} coord A Coordinate.
        253 * @return {number} The y position of {@code coord} relative to the nearest
        254 * side of {@code box}, or zero if {@code coord} is inside {@code box}.
        255 */
        256goog.math.Box.relativePositionY = function(box, coord) {
        257 if (coord.y < box.top) {
        258 return coord.y - box.top;
        259 } else if (coord.y > box.bottom) {
        260 return coord.y - box.bottom;
        261 }
        262 return 0;
        263};
        264
        265
        266/**
        267 * Returns the distance between a coordinate and the nearest corner/side of a
        268 * box. Returns zero if the coordinate is inside the box.
        269 *
        270 * @param {goog.math.Box} box A Box.
        271 * @param {goog.math.Coordinate} coord A Coordinate.
        272 * @return {number} The distance between {@code coord} and the nearest
        273 * corner/side of {@code box}, or zero if {@code coord} is inside
        274 * {@code box}.
        275 */
        276goog.math.Box.distance = function(box, coord) {
        277 var x = goog.math.Box.relativePositionX(box, coord);
        278 var y = goog.math.Box.relativePositionY(box, coord);
        279 return Math.sqrt(x * x + y * y);
        280};
        281
        282
        283/**
        284 * Returns whether two boxes intersect.
        285 *
        286 * @param {goog.math.Box} a A Box.
        287 * @param {goog.math.Box} b A second Box.
        288 * @return {boolean} Whether the boxes intersect.
        289 */
        290goog.math.Box.intersects = function(a, b) {
        291 return (a.left <= b.right && b.left <= a.right &&
        292 a.top <= b.bottom && b.top <= a.bottom);
        293};
        294
        295
        296/**
        297 * Returns whether two boxes would intersect with additional padding.
        298 *
        299 * @param {goog.math.Box} a A Box.
        300 * @param {goog.math.Box} b A second Box.
        301 * @param {number} padding The additional padding.
        302 * @return {boolean} Whether the boxes intersect.
        303 */
        304goog.math.Box.intersectsWithPadding = function(a, b, padding) {
        305 return (a.left <= b.right + padding && b.left <= a.right + padding &&
        306 a.top <= b.bottom + padding && b.top <= a.bottom + padding);
        307};
        308
        309
        310/**
        311 * Rounds the fields to the next larger integer values.
        312 *
        313 * @return {!goog.math.Box} This box with ceil'd fields.
        314 */
        315goog.math.Box.prototype.ceil = function() {
        316 this.top = Math.ceil(this.top);
        317 this.right = Math.ceil(this.right);
        318 this.bottom = Math.ceil(this.bottom);
        319 this.left = Math.ceil(this.left);
        320 return this;
        321};
        322
        323
        324/**
        325 * Rounds the fields to the next smaller integer values.
        326 *
        327 * @return {!goog.math.Box} This box with floored fields.
        328 */
        329goog.math.Box.prototype.floor = function() {
        330 this.top = Math.floor(this.top);
        331 this.right = Math.floor(this.right);
        332 this.bottom = Math.floor(this.bottom);
        333 this.left = Math.floor(this.left);
        334 return this;
        335};
        336
        337
        338/**
        339 * Rounds the fields to nearest integer values.
        340 *
        341 * @return {!goog.math.Box} This box with rounded fields.
        342 */
        343goog.math.Box.prototype.round = function() {
        344 this.top = Math.round(this.top);
        345 this.right = Math.round(this.right);
        346 this.bottom = Math.round(this.bottom);
        347 this.left = Math.round(this.left);
        348 return this;
        349};
        350
        351
        352/**
        353 * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
        354 * is given, then the left and right values are translated by the coordinate's
        355 * x value and the top and bottom values are translated by the coordinate's y
        356 * value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
        357 * and y dimension values.
        358 *
        359 * @param {number|goog.math.Coordinate} tx The value to translate the x
        360 * dimension values by or the the coordinate to translate this box by.
        361 * @param {number=} opt_ty The value to translate y dimension values by.
        362 * @return {!goog.math.Box} This box after translating.
        363 */
        364goog.math.Box.prototype.translate = function(tx, opt_ty) {
        365 if (tx instanceof goog.math.Coordinate) {
        366 this.left += tx.x;
        367 this.right += tx.x;
        368 this.top += tx.y;
        369 this.bottom += tx.y;
        370 } else {
        371 this.left += tx;
        372 this.right += tx;
        373 if (goog.isNumber(opt_ty)) {
        374 this.top += opt_ty;
        375 this.bottom += opt_ty;
        376 }
        377 }
        378 return this;
        379};
        380
        381
        382/**
        383 * Scales this coordinate by the given scale factors. The x and y dimension
        384 * values are scaled by {@code sx} and {@code opt_sy} respectively.
        385 * If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
        386 *
        387 * @param {number} sx The scale factor to use for the x dimension.
        388 * @param {number=} opt_sy The scale factor to use for the y dimension.
        389 * @return {!goog.math.Box} This box after scaling.
        390 */
        391goog.math.Box.prototype.scale = function(sx, opt_sy) {
        392 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
        393 this.left *= sx;
        394 this.right *= sx;
        395 this.top *= sy;
        396 this.bottom *= sy;
        397 return this;
        398};
        \ No newline at end of file diff --git a/docs/source/lib/goog/math/coordinate.js.src.html b/docs/source/lib/goog/math/coordinate.js.src.html new file mode 100644 index 0000000..3fcf521 --- /dev/null +++ b/docs/source/lib/goog/math/coordinate.js.src.html @@ -0,0 +1 @@ +coordinate.js

        lib/goog/math/coordinate.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 A utility class for representing two-dimensional positions.
        17 */
        18
        19
        20goog.provide('goog.math.Coordinate');
        21
        22goog.require('goog.math');
        23
        24
        25
        26/**
        27 * Class for representing coordinates and positions.
        28 * @param {number=} opt_x Left, defaults to 0.
        29 * @param {number=} opt_y Top, defaults to 0.
        30 * @struct
        31 * @constructor
        32 */
        33goog.math.Coordinate = function(opt_x, opt_y) {
        34 /**
        35 * X-value
        36 * @type {number}
        37 */
        38 this.x = goog.isDef(opt_x) ? opt_x : 0;
        39
        40 /**
        41 * Y-value
        42 * @type {number}
        43 */
        44 this.y = goog.isDef(opt_y) ? opt_y : 0;
        45};
        46
        47
        48/**
        49 * Returns a new copy of the coordinate.
        50 * @return {!goog.math.Coordinate} A clone of this coordinate.
        51 */
        52goog.math.Coordinate.prototype.clone = function() {
        53 return new goog.math.Coordinate(this.x, this.y);
        54};
        55
        56
        57if (goog.DEBUG) {
        58 /**
        59 * Returns a nice string representing the coordinate.
        60 * @return {string} In the form (50, 73).
        61 * @override
        62 */
        63 goog.math.Coordinate.prototype.toString = function() {
        64 return '(' + this.x + ', ' + this.y + ')';
        65 };
        66}
        67
        68
        69/**
        70 * Compares coordinates for equality.
        71 * @param {goog.math.Coordinate} a A Coordinate.
        72 * @param {goog.math.Coordinate} b A Coordinate.
        73 * @return {boolean} True iff the coordinates are equal, or if both are null.
        74 */
        75goog.math.Coordinate.equals = function(a, b) {
        76 if (a == b) {
        77 return true;
        78 }
        79 if (!a || !b) {
        80 return false;
        81 }
        82 return a.x == b.x && a.y == b.y;
        83};
        84
        85
        86/**
        87 * Returns the distance between two coordinates.
        88 * @param {!goog.math.Coordinate} a A Coordinate.
        89 * @param {!goog.math.Coordinate} b A Coordinate.
        90 * @return {number} The distance between {@code a} and {@code b}.
        91 */
        92goog.math.Coordinate.distance = function(a, b) {
        93 var dx = a.x - b.x;
        94 var dy = a.y - b.y;
        95 return Math.sqrt(dx * dx + dy * dy);
        96};
        97
        98
        99/**
        100 * Returns the magnitude of a coordinate.
        101 * @param {!goog.math.Coordinate} a A Coordinate.
        102 * @return {number} The distance between the origin and {@code a}.
        103 */
        104goog.math.Coordinate.magnitude = function(a) {
        105 return Math.sqrt(a.x * a.x + a.y * a.y);
        106};
        107
        108
        109/**
        110 * Returns the angle from the origin to a coordinate.
        111 * @param {!goog.math.Coordinate} a A Coordinate.
        112 * @return {number} The angle, in degrees, clockwise from the positive X
        113 * axis to {@code a}.
        114 */
        115goog.math.Coordinate.azimuth = function(a) {
        116 return goog.math.angle(0, 0, a.x, a.y);
        117};
        118
        119
        120/**
        121 * Returns the squared distance between two coordinates. Squared distances can
        122 * be used for comparisons when the actual value is not required.
        123 *
        124 * Performance note: eliminating the square root is an optimization often used
        125 * in lower-level languages, but the speed difference is not nearly as
        126 * pronounced in JavaScript (only a few percent.)
        127 *
        128 * @param {!goog.math.Coordinate} a A Coordinate.
        129 * @param {!goog.math.Coordinate} b A Coordinate.
        130 * @return {number} The squared distance between {@code a} and {@code b}.
        131 */
        132goog.math.Coordinate.squaredDistance = function(a, b) {
        133 var dx = a.x - b.x;
        134 var dy = a.y - b.y;
        135 return dx * dx + dy * dy;
        136};
        137
        138
        139/**
        140 * Returns the difference between two coordinates as a new
        141 * goog.math.Coordinate.
        142 * @param {!goog.math.Coordinate} a A Coordinate.
        143 * @param {!goog.math.Coordinate} b A Coordinate.
        144 * @return {!goog.math.Coordinate} A Coordinate representing the difference
        145 * between {@code a} and {@code b}.
        146 */
        147goog.math.Coordinate.difference = function(a, b) {
        148 return new goog.math.Coordinate(a.x - b.x, a.y - b.y);
        149};
        150
        151
        152/**
        153 * Returns the sum of two coordinates as a new goog.math.Coordinate.
        154 * @param {!goog.math.Coordinate} a A Coordinate.
        155 * @param {!goog.math.Coordinate} b A Coordinate.
        156 * @return {!goog.math.Coordinate} A Coordinate representing the sum of the two
        157 * coordinates.
        158 */
        159goog.math.Coordinate.sum = function(a, b) {
        160 return new goog.math.Coordinate(a.x + b.x, a.y + b.y);
        161};
        162
        163
        164/**
        165 * Rounds the x and y fields to the next larger integer values.
        166 * @return {!goog.math.Coordinate} This coordinate with ceil'd fields.
        167 */
        168goog.math.Coordinate.prototype.ceil = function() {
        169 this.x = Math.ceil(this.x);
        170 this.y = Math.ceil(this.y);
        171 return this;
        172};
        173
        174
        175/**
        176 * Rounds the x and y fields to the next smaller integer values.
        177 * @return {!goog.math.Coordinate} This coordinate with floored fields.
        178 */
        179goog.math.Coordinate.prototype.floor = function() {
        180 this.x = Math.floor(this.x);
        181 this.y = Math.floor(this.y);
        182 return this;
        183};
        184
        185
        186/**
        187 * Rounds the x and y fields to the nearest integer values.
        188 * @return {!goog.math.Coordinate} This coordinate with rounded fields.
        189 */
        190goog.math.Coordinate.prototype.round = function() {
        191 this.x = Math.round(this.x);
        192 this.y = Math.round(this.y);
        193 return this;
        194};
        195
        196
        197/**
        198 * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
        199 * is given, then the x and y values are translated by the coordinate's x and y.
        200 * Otherwise, x and y are translated by {@code tx} and {@code opt_ty}
        201 * respectively.
        202 * @param {number|goog.math.Coordinate} tx The value to translate x by or the
        203 * the coordinate to translate this coordinate by.
        204 * @param {number=} opt_ty The value to translate y by.
        205 * @return {!goog.math.Coordinate} This coordinate after translating.
        206 */
        207goog.math.Coordinate.prototype.translate = function(tx, opt_ty) {
        208 if (tx instanceof goog.math.Coordinate) {
        209 this.x += tx.x;
        210 this.y += tx.y;
        211 } else {
        212 this.x += tx;
        213 if (goog.isNumber(opt_ty)) {
        214 this.y += opt_ty;
        215 }
        216 }
        217 return this;
        218};
        219
        220
        221/**
        222 * Scales this coordinate by the given scale factors. The x and y values are
        223 * scaled by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy}
        224 * is not given, then {@code sx} is used for both x and y.
        225 * @param {number} sx The scale factor to use for the x dimension.
        226 * @param {number=} opt_sy The scale factor to use for the y dimension.
        227 * @return {!goog.math.Coordinate} This coordinate after scaling.
        228 */
        229goog.math.Coordinate.prototype.scale = function(sx, opt_sy) {
        230 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
        231 this.x *= sx;
        232 this.y *= sy;
        233 return this;
        234};
        235
        236
        237/**
        238 * Rotates this coordinate clockwise about the origin (or, optionally, the given
        239 * center) by the given angle, in radians.
        240 * @param {number} radians The angle by which to rotate this coordinate
        241 * clockwise about the given center, in radians.
        242 * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
        243 * to (0, 0) if not given.
        244 */
        245goog.math.Coordinate.prototype.rotateRadians = function(radians, opt_center) {
        246 var center = opt_center || new goog.math.Coordinate(0, 0);
        247
        248 var x = this.x;
        249 var y = this.y;
        250 var cos = Math.cos(radians);
        251 var sin = Math.sin(radians);
        252
        253 this.x = (x - center.x) * cos - (y - center.y) * sin + center.x;
        254 this.y = (x - center.x) * sin + (y - center.y) * cos + center.y;
        255};
        256
        257
        258/**
        259 * Rotates this coordinate clockwise about the origin (or, optionally, the given
        260 * center) by the given angle, in degrees.
        261 * @param {number} degrees The angle by which to rotate this coordinate
        262 * clockwise about the given center, in degrees.
        263 * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
        264 * to (0, 0) if not given.
        265 */
        266goog.math.Coordinate.prototype.rotateDegrees = function(degrees, opt_center) {
        267 this.rotateRadians(goog.math.toRadians(degrees), opt_center);
        268};
        \ No newline at end of file diff --git a/docs/source/lib/goog/math/math.js.src.html b/docs/source/lib/goog/math/math.js.src.html new file mode 100644 index 0000000..fa0d7c3 --- /dev/null +++ b/docs/source/lib/goog/math/math.js.src.html @@ -0,0 +1 @@ +math.js

        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
        19goog.provide('goog.math');
        20
        21goog.require('goog.array');
        22goog.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 */
        30goog.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 */
        42goog.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 */
        55goog.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 */
        73goog.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 */
        89goog.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 */
        103goog.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 */
        116goog.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 */
        127goog.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 */
        137goog.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 */
        147goog.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 */
        158goog.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 */
        169goog.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 */
        185goog.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 */
        205goog.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. Preserves
        221 * signed zeros and NaN.
        222 */
        223goog.math.sign = Math.sign || function(x) {
        224 if (x > 0) {
        225 return 1;
        226 }
        227 if (x < 0) {
        228 return -1;
        229 }
        230 return x; // Preserves signed zeros and NaN.
        231};
        232
        233
        234/**
        235 * JavaScript implementation of Longest Common Subsequence problem.
        236 * http://en.wikipedia.org/wiki/Longest_common_subsequence
        237 *
        238 * Returns the longest possible array that is subarray of both of given arrays.
        239 *
        240 * @param {Array<Object>} array1 First array of objects.
        241 * @param {Array<Object>} array2 Second array of objects.
        242 * @param {Function=} opt_compareFn Function that acts as a custom comparator
        243 * for the array ojects. Function should return true if objects are equal,
        244 * otherwise false.
        245 * @param {Function=} opt_collectorFn Function used to decide what to return
        246 * as a result subsequence. It accepts 2 arguments: index of common element
        247 * in the first array and index in the second. The default function returns
        248 * element from the first array.
        249 * @return {!Array<Object>} A list of objects that are common to both arrays
        250 * such that there is no common subsequence with size greater than the
        251 * length of the list.
        252 */
        253goog.math.longestCommonSubsequence = function(
        254 array1, array2, opt_compareFn, opt_collectorFn) {
        255
        256 var compare = opt_compareFn || function(a, b) {
        257 return a == b;
        258 };
        259
        260 var collect = opt_collectorFn || function(i1, i2) {
        261 return array1[i1];
        262 };
        263
        264 var length1 = array1.length;
        265 var length2 = array2.length;
        266
        267 var arr = [];
        268 for (var i = 0; i < length1 + 1; i++) {
        269 arr[i] = [];
        270 arr[i][0] = 0;
        271 }
        272
        273 for (var j = 0; j < length2 + 1; j++) {
        274 arr[0][j] = 0;
        275 }
        276
        277 for (i = 1; i <= length1; i++) {
        278 for (j = 1; j <= length2; j++) {
        279 if (compare(array1[i - 1], array2[j - 1])) {
        280 arr[i][j] = arr[i - 1][j - 1] + 1;
        281 } else {
        282 arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
        283 }
        284 }
        285 }
        286
        287 // Backtracking
        288 var result = [];
        289 var i = length1, j = length2;
        290 while (i > 0 && j > 0) {
        291 if (compare(array1[i - 1], array2[j - 1])) {
        292 result.unshift(collect(i - 1, j - 1));
        293 i--;
        294 j--;
        295 } else {
        296 if (arr[i - 1][j] > arr[i][j - 1]) {
        297 i--;
        298 } else {
        299 j--;
        300 }
        301 }
        302 }
        303
        304 return result;
        305};
        306
        307
        308/**
        309 * Returns the sum of the arguments.
        310 * @param {...number} var_args Numbers to add.
        311 * @return {number} The sum of the arguments (0 if no arguments were provided,
        312 * {@code NaN} if any of the arguments is not a valid number).
        313 */
        314goog.math.sum = function(var_args) {
        315 return /** @type {number} */ (goog.array.reduce(arguments,
        316 function(sum, value) {
        317 return sum + value;
        318 }, 0));
        319};
        320
        321
        322/**
        323 * Returns the arithmetic mean of the arguments.
        324 * @param {...number} var_args Numbers to average.
        325 * @return {number} The average of the arguments ({@code NaN} if no arguments
        326 * were provided or any of the arguments is not a valid number).
        327 */
        328goog.math.average = function(var_args) {
        329 return goog.math.sum.apply(null, arguments) / arguments.length;
        330};
        331
        332
        333/**
        334 * Returns the unbiased sample variance of the arguments. For a definition,
        335 * see e.g. http://en.wikipedia.org/wiki/Variance
        336 * @param {...number} var_args Number samples to analyze.
        337 * @return {number} The unbiased sample variance of the arguments (0 if fewer
        338 * than two samples were provided, or {@code NaN} if any of the samples is
        339 * not a valid number).
        340 */
        341goog.math.sampleVariance = function(var_args) {
        342 var sampleSize = arguments.length;
        343 if (sampleSize < 2) {
        344 return 0;
        345 }
        346
        347 var mean = goog.math.average.apply(null, arguments);
        348 var variance = goog.math.sum.apply(null, goog.array.map(arguments,
        349 function(val) {
        350 return Math.pow(val - mean, 2);
        351 })) / (sampleSize - 1);
        352
        353 return variance;
        354};
        355
        356
        357/**
        358 * Returns the sample standard deviation of the arguments. For a definition of
        359 * sample standard deviation, see e.g.
        360 * http://en.wikipedia.org/wiki/Standard_deviation
        361 * @param {...number} var_args Number samples to analyze.
        362 * @return {number} The sample standard deviation of the arguments (0 if fewer
        363 * than two samples were provided, or {@code NaN} if any of the samples is
        364 * not a valid number).
        365 */
        366goog.math.standardDeviation = function(var_args) {
        367 return Math.sqrt(goog.math.sampleVariance.apply(null, arguments));
        368};
        369
        370
        371/**
        372 * Returns whether the supplied number represents an integer, i.e. that is has
        373 * no fractional component. No range-checking is performed on the number.
        374 * @param {number} num The number to test.
        375 * @return {boolean} Whether {@code num} is an integer.
        376 */
        377goog.math.isInt = function(num) {
        378 return isFinite(num) && num % 1 == 0;
        379};
        380
        381
        382/**
        383 * Returns whether the supplied number is finite and not NaN.
        384 * @param {number} num The number to test.
        385 * @return {boolean} Whether {@code num} is a finite number.
        386 */
        387goog.math.isFiniteNumber = function(num) {
        388 return isFinite(num) && !isNaN(num);
        389};
        390
        391
        392/**
        393 * @param {number} num The number to test.
        394 * @return {boolean} Whether it is negative zero.
        395 */
        396goog.math.isNegativeZero = function(num) {
        397 return num == 0 && 1 / num < 0;
        398};
        399
        400
        401/**
        402 * Returns the precise value of floor(log10(num)).
        403 * Simpler implementations didn't work because of floating point rounding
        404 * errors. For example
        405 * <ul>
        406 * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3.
        407 * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15.
        408 * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1.
        409 * </ul>
        410 * @param {number} num A floating point number.
        411 * @return {number} Its logarithm to base 10 rounded down to the nearest
        412 * integer if num > 0. -Infinity if num == 0. NaN if num < 0.
        413 */
        414goog.math.log10Floor = function(num) {
        415 if (num > 0) {
        416 var x = Math.round(Math.log(num) * Math.LOG10E);
        417 return x - (parseFloat('1e' + x) > num ? 1 : 0);
        418 }
        419 return num == 0 ? -Infinity : NaN;
        420};
        421
        422
        423/**
        424 * A tweaked variant of {@code Math.floor} which tolerates if the passed number
        425 * is infinitesimally smaller than the closest integer. It often happens with
        426 * the results of floating point calculations because of the finite precision
        427 * of the intermediate results. For example {@code Math.floor(Math.log(1000) /
        428 * Math.LN10) == 2}, not 3 as one would expect.
        429 * @param {number} num A number.
        430 * @param {number=} opt_epsilon An infinitesimally small positive number, the
        431 * rounding error to tolerate.
        432 * @return {number} The largest integer less than or equal to {@code num}.
        433 */
        434goog.math.safeFloor = function(num, opt_epsilon) {
        435 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
        436 return Math.floor(num + (opt_epsilon || 2e-15));
        437};
        438
        439
        440/**
        441 * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
        442 * details.
        443 * @param {number} num A number.
        444 * @param {number=} opt_epsilon An infinitesimally small positive number, the
        445 * rounding error to tolerate.
        446 * @return {number} The smallest integer greater than or equal to {@code num}.
        447 */
        448goog.math.safeCeil = function(num, opt_epsilon) {
        449 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
        450 return Math.ceil(num - (opt_epsilon || 2e-15));
        451};
        \ No newline at end of file diff --git a/docs/source/lib/goog/math/rect.js.src.html b/docs/source/lib/goog/math/rect.js.src.html new file mode 100644 index 0000000..0ee35fb --- /dev/null +++ b/docs/source/lib/goog/math/rect.js.src.html @@ -0,0 +1 @@ +rect.js

        lib/goog/math/rect.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 A utility class for representing rectangles.
        17 */
        18
        19goog.provide('goog.math.Rect');
        20
        21goog.require('goog.math.Box');
        22goog.require('goog.math.Coordinate');
        23goog.require('goog.math.Size');
        24
        25
        26
        27/**
        28 * Class for representing rectangular regions.
        29 * @param {number} x Left.
        30 * @param {number} y Top.
        31 * @param {number} w Width.
        32 * @param {number} h Height.
        33 * @struct
        34 * @constructor
        35 */
        36goog.math.Rect = function(x, y, w, h) {
        37 /** @type {number} */
        38 this.left = x;
        39
        40 /** @type {number} */
        41 this.top = y;
        42
        43 /** @type {number} */
        44 this.width = w;
        45
        46 /** @type {number} */
        47 this.height = h;
        48};
        49
        50
        51/**
        52 * @return {!goog.math.Rect} A new copy of this Rectangle.
        53 */
        54goog.math.Rect.prototype.clone = function() {
        55 return new goog.math.Rect(this.left, this.top, this.width, this.height);
        56};
        57
        58
        59/**
        60 * Returns a new Box object with the same position and dimensions as this
        61 * rectangle.
        62 * @return {!goog.math.Box} A new Box representation of this Rectangle.
        63 */
        64goog.math.Rect.prototype.toBox = function() {
        65 var right = this.left + this.width;
        66 var bottom = this.top + this.height;
        67 return new goog.math.Box(this.top,
        68 right,
        69 bottom,
        70 this.left);
        71};
        72
        73
        74/**
        75 * Creates a new Rect object with the position and size given.
        76 * @param {!goog.math.Coordinate} position The top-left coordinate of the Rect
        77 * @param {!goog.math.Size} size The size of the Rect
        78 * @return {!goog.math.Rect} A new Rect initialized with the given position and
        79 * size.
        80 */
        81goog.math.Rect.createFromPositionAndSize = function(position, size) {
        82 return new goog.math.Rect(position.x, position.y, size.width, size.height);
        83};
        84
        85
        86/**
        87 * Creates a new Rect object with the same position and dimensions as a given
        88 * Box. Note that this is only the inverse of toBox if left/top are defined.
        89 * @param {goog.math.Box} box A box.
        90 * @return {!goog.math.Rect} A new Rect initialized with the box's position
        91 * and size.
        92 */
        93goog.math.Rect.createFromBox = function(box) {
        94 return new goog.math.Rect(box.left, box.top,
        95 box.right - box.left, box.bottom - box.top);
        96};
        97
        98
        99if (goog.DEBUG) {
        100 /**
        101 * Returns a nice string representing size and dimensions of rectangle.
        102 * @return {string} In the form (50, 73 - 75w x 25h).
        103 * @override
        104 */
        105 goog.math.Rect.prototype.toString = function() {
        106 return '(' + this.left + ', ' + this.top + ' - ' + this.width + 'w x ' +
        107 this.height + 'h)';
        108 };
        109}
        110
        111
        112/**
        113 * Compares rectangles for equality.
        114 * @param {goog.math.Rect} a A Rectangle.
        115 * @param {goog.math.Rect} b A Rectangle.
        116 * @return {boolean} True iff the rectangles have the same left, top, width,
        117 * and height, or if both are null.
        118 */
        119goog.math.Rect.equals = function(a, b) {
        120 if (a == b) {
        121 return true;
        122 }
        123 if (!a || !b) {
        124 return false;
        125 }
        126 return a.left == b.left && a.width == b.width &&
        127 a.top == b.top && a.height == b.height;
        128};
        129
        130
        131/**
        132 * Computes the intersection of this rectangle and the rectangle parameter. If
        133 * there is no intersection, returns false and leaves this rectangle as is.
        134 * @param {goog.math.Rect} rect A Rectangle.
        135 * @return {boolean} True iff this rectangle intersects with the parameter.
        136 */
        137goog.math.Rect.prototype.intersection = function(rect) {
        138 var x0 = Math.max(this.left, rect.left);
        139 var x1 = Math.min(this.left + this.width, rect.left + rect.width);
        140
        141 if (x0 <= x1) {
        142 var y0 = Math.max(this.top, rect.top);
        143 var y1 = Math.min(this.top + this.height, rect.top + rect.height);
        144
        145 if (y0 <= y1) {
        146 this.left = x0;
        147 this.top = y0;
        148 this.width = x1 - x0;
        149 this.height = y1 - y0;
        150
        151 return true;
        152 }
        153 }
        154 return false;
        155};
        156
        157
        158/**
        159 * Returns the intersection of two rectangles. Two rectangles intersect if they
        160 * touch at all, for example, two zero width and height rectangles would
        161 * intersect if they had the same top and left.
        162 * @param {goog.math.Rect} a A Rectangle.
        163 * @param {goog.math.Rect} b A Rectangle.
        164 * @return {goog.math.Rect} A new intersection rect (even if width and height
        165 * are 0), or null if there is no intersection.
        166 */
        167goog.math.Rect.intersection = function(a, b) {
        168 // There is no nice way to do intersection via a clone, because any such
        169 // clone might be unnecessary if this function returns null. So, we duplicate
        170 // code from above.
        171
        172 var x0 = Math.max(a.left, b.left);
        173 var x1 = Math.min(a.left + a.width, b.left + b.width);
        174
        175 if (x0 <= x1) {
        176 var y0 = Math.max(a.top, b.top);
        177 var y1 = Math.min(a.top + a.height, b.top + b.height);
        178
        179 if (y0 <= y1) {
        180 return new goog.math.Rect(x0, y0, x1 - x0, y1 - y0);
        181 }
        182 }
        183 return null;
        184};
        185
        186
        187/**
        188 * Returns whether two rectangles intersect. Two rectangles intersect if they
        189 * touch at all, for example, two zero width and height rectangles would
        190 * intersect if they had the same top and left.
        191 * @param {goog.math.Rect} a A Rectangle.
        192 * @param {goog.math.Rect} b A Rectangle.
        193 * @return {boolean} Whether a and b intersect.
        194 */
        195goog.math.Rect.intersects = function(a, b) {
        196 return (a.left <= b.left + b.width && b.left <= a.left + a.width &&
        197 a.top <= b.top + b.height && b.top <= a.top + a.height);
        198};
        199
        200
        201/**
        202 * Returns whether a rectangle intersects this rectangle.
        203 * @param {goog.math.Rect} rect A rectangle.
        204 * @return {boolean} Whether rect intersects this rectangle.
        205 */
        206goog.math.Rect.prototype.intersects = function(rect) {
        207 return goog.math.Rect.intersects(this, rect);
        208};
        209
        210
        211/**
        212 * Computes the difference regions between two rectangles. The return value is
        213 * an array of 0 to 4 rectangles defining the remaining regions of the first
        214 * rectangle after the second has been subtracted.
        215 * @param {goog.math.Rect} a A Rectangle.
        216 * @param {goog.math.Rect} b A Rectangle.
        217 * @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
        218 * together define the difference area of rectangle a minus rectangle b.
        219 */
        220goog.math.Rect.difference = function(a, b) {
        221 var intersection = goog.math.Rect.intersection(a, b);
        222 if (!intersection || !intersection.height || !intersection.width) {
        223 return [a.clone()];
        224 }
        225
        226 var result = [];
        227
        228 var top = a.top;
        229 var height = a.height;
        230
        231 var ar = a.left + a.width;
        232 var ab = a.top + a.height;
        233
        234 var br = b.left + b.width;
        235 var bb = b.top + b.height;
        236
        237 // Subtract off any area on top where A extends past B
        238 if (b.top > a.top) {
        239 result.push(new goog.math.Rect(a.left, a.top, a.width, b.top - a.top));
        240 top = b.top;
        241 // If we're moving the top down, we also need to subtract the height diff.
        242 height -= b.top - a.top;
        243 }
        244 // Subtract off any area on bottom where A extends past B
        245 if (bb < ab) {
        246 result.push(new goog.math.Rect(a.left, bb, a.width, ab - bb));
        247 height = bb - top;
        248 }
        249 // Subtract any area on left where A extends past B
        250 if (b.left > a.left) {
        251 result.push(new goog.math.Rect(a.left, top, b.left - a.left, height));
        252 }
        253 // Subtract any area on right where A extends past B
        254 if (br < ar) {
        255 result.push(new goog.math.Rect(br, top, ar - br, height));
        256 }
        257
        258 return result;
        259};
        260
        261
        262/**
        263 * Computes the difference regions between this rectangle and {@code rect}. The
        264 * return value is an array of 0 to 4 rectangles defining the remaining regions
        265 * of this rectangle after the other has been subtracted.
        266 * @param {goog.math.Rect} rect A Rectangle.
        267 * @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
        268 * together define the difference area of rectangle a minus rectangle b.
        269 */
        270goog.math.Rect.prototype.difference = function(rect) {
        271 return goog.math.Rect.difference(this, rect);
        272};
        273
        274
        275/**
        276 * Expand this rectangle to also include the area of the given rectangle.
        277 * @param {goog.math.Rect} rect The other rectangle.
        278 */
        279goog.math.Rect.prototype.boundingRect = function(rect) {
        280 // We compute right and bottom before we change left and top below.
        281 var right = Math.max(this.left + this.width, rect.left + rect.width);
        282 var bottom = Math.max(this.top + this.height, rect.top + rect.height);
        283
        284 this.left = Math.min(this.left, rect.left);
        285 this.top = Math.min(this.top, rect.top);
        286
        287 this.width = right - this.left;
        288 this.height = bottom - this.top;
        289};
        290
        291
        292/**
        293 * Returns a new rectangle which completely contains both input rectangles.
        294 * @param {goog.math.Rect} a A rectangle.
        295 * @param {goog.math.Rect} b A rectangle.
        296 * @return {goog.math.Rect} A new bounding rect, or null if either rect is
        297 * null.
        298 */
        299goog.math.Rect.boundingRect = function(a, b) {
        300 if (!a || !b) {
        301 return null;
        302 }
        303
        304 var clone = a.clone();
        305 clone.boundingRect(b);
        306
        307 return clone;
        308};
        309
        310
        311/**
        312 * Tests whether this rectangle entirely contains another rectangle or
        313 * coordinate.
        314 *
        315 * @param {goog.math.Rect|goog.math.Coordinate} another The rectangle or
        316 * coordinate to test for containment.
        317 * @return {boolean} Whether this rectangle contains given rectangle or
        318 * coordinate.
        319 */
        320goog.math.Rect.prototype.contains = function(another) {
        321 if (another instanceof goog.math.Rect) {
        322 return this.left <= another.left &&
        323 this.left + this.width >= another.left + another.width &&
        324 this.top <= another.top &&
        325 this.top + this.height >= another.top + another.height;
        326 } else { // (another instanceof goog.math.Coordinate)
        327 return another.x >= this.left &&
        328 another.x <= this.left + this.width &&
        329 another.y >= this.top &&
        330 another.y <= this.top + this.height;
        331 }
        332};
        333
        334
        335/**
        336 * @param {!goog.math.Coordinate} point A coordinate.
        337 * @return {number} The squared distance between the point and the closest
        338 * point inside the rectangle. Returns 0 if the point is inside the
        339 * rectangle.
        340 */
        341goog.math.Rect.prototype.squaredDistance = function(point) {
        342 var dx = point.x < this.left ?
        343 this.left - point.x : Math.max(point.x - (this.left + this.width), 0);
        344 var dy = point.y < this.top ?
        345 this.top - point.y : Math.max(point.y - (this.top + this.height), 0);
        346 return dx * dx + dy * dy;
        347};
        348
        349
        350/**
        351 * @param {!goog.math.Coordinate} point A coordinate.
        352 * @return {number} The distance between the point and the closest point
        353 * inside the rectangle. Returns 0 if the point is inside the rectangle.
        354 */
        355goog.math.Rect.prototype.distance = function(point) {
        356 return Math.sqrt(this.squaredDistance(point));
        357};
        358
        359
        360/**
        361 * @return {!goog.math.Size} The size of this rectangle.
        362 */
        363goog.math.Rect.prototype.getSize = function() {
        364 return new goog.math.Size(this.width, this.height);
        365};
        366
        367
        368/**
        369 * @return {!goog.math.Coordinate} A new coordinate for the top-left corner of
        370 * the rectangle.
        371 */
        372goog.math.Rect.prototype.getTopLeft = function() {
        373 return new goog.math.Coordinate(this.left, this.top);
        374};
        375
        376
        377/**
        378 * @return {!goog.math.Coordinate} A new coordinate for the center of the
        379 * rectangle.
        380 */
        381goog.math.Rect.prototype.getCenter = function() {
        382 return new goog.math.Coordinate(
        383 this.left + this.width / 2, this.top + this.height / 2);
        384};
        385
        386
        387/**
        388 * @return {!goog.math.Coordinate} A new coordinate for the bottom-right corner
        389 * of the rectangle.
        390 */
        391goog.math.Rect.prototype.getBottomRight = function() {
        392 return new goog.math.Coordinate(
        393 this.left + this.width, this.top + this.height);
        394};
        395
        396
        397/**
        398 * Rounds the fields to the next larger integer values.
        399 * @return {!goog.math.Rect} This rectangle with ceil'd fields.
        400 */
        401goog.math.Rect.prototype.ceil = function() {
        402 this.left = Math.ceil(this.left);
        403 this.top = Math.ceil(this.top);
        404 this.width = Math.ceil(this.width);
        405 this.height = Math.ceil(this.height);
        406 return this;
        407};
        408
        409
        410/**
        411 * Rounds the fields to the next smaller integer values.
        412 * @return {!goog.math.Rect} This rectangle with floored fields.
        413 */
        414goog.math.Rect.prototype.floor = function() {
        415 this.left = Math.floor(this.left);
        416 this.top = Math.floor(this.top);
        417 this.width = Math.floor(this.width);
        418 this.height = Math.floor(this.height);
        419 return this;
        420};
        421
        422
        423/**
        424 * Rounds the fields to nearest integer values.
        425 * @return {!goog.math.Rect} This rectangle with rounded fields.
        426 */
        427goog.math.Rect.prototype.round = function() {
        428 this.left = Math.round(this.left);
        429 this.top = Math.round(this.top);
        430 this.width = Math.round(this.width);
        431 this.height = Math.round(this.height);
        432 return this;
        433};
        434
        435
        436/**
        437 * Translates this rectangle by the given offsets. If a
        438 * {@code goog.math.Coordinate} is given, then the left and top values are
        439 * translated by the coordinate's x and y values. Otherwise, top and left are
        440 * translated by {@code tx} and {@code opt_ty} respectively.
        441 * @param {number|goog.math.Coordinate} tx The value to translate left by or the
        442 * the coordinate to translate this rect by.
        443 * @param {number=} opt_ty The value to translate top by.
        444 * @return {!goog.math.Rect} This rectangle after translating.
        445 */
        446goog.math.Rect.prototype.translate = function(tx, opt_ty) {
        447 if (tx instanceof goog.math.Coordinate) {
        448 this.left += tx.x;
        449 this.top += tx.y;
        450 } else {
        451 this.left += tx;
        452 if (goog.isNumber(opt_ty)) {
        453 this.top += opt_ty;
        454 }
        455 }
        456 return this;
        457};
        458
        459
        460/**
        461 * Scales this rectangle by the given scale factors. The left and width values
        462 * are scaled by {@code sx} and the top and height values are scaled by
        463 * {@code opt_sy}. If {@code opt_sy} is not given, then all fields are scaled
        464 * by {@code sx}.
        465 * @param {number} sx The scale factor to use for the x dimension.
        466 * @param {number=} opt_sy The scale factor to use for the y dimension.
        467 * @return {!goog.math.Rect} This rectangle after scaling.
        468 */
        469goog.math.Rect.prototype.scale = function(sx, opt_sy) {
        470 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
        471 this.left *= sx;
        472 this.width *= sx;
        473 this.top *= sy;
        474 this.height *= sy;
        475 return this;
        476};
        \ No newline at end of file diff --git a/docs/source/lib/goog/math/size.js.src.html b/docs/source/lib/goog/math/size.js.src.html new file mode 100644 index 0000000..d35a7f5 --- /dev/null +++ b/docs/source/lib/goog/math/size.js.src.html @@ -0,0 +1 @@ +size.js

        lib/goog/math/size.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 A utility class for representing two-dimensional sizes.
        17 * @author brenneman@google.com (Shawn Brenneman)
        18 */
        19
        20
        21goog.provide('goog.math.Size');
        22
        23
        24
        25/**
        26 * Class for representing sizes consisting of a width and height. Undefined
        27 * width and height support is deprecated and results in compiler warning.
        28 * @param {number} width Width.
        29 * @param {number} height Height.
        30 * @struct
        31 * @constructor
        32 */
        33goog.math.Size = function(width, height) {
        34 /**
        35 * Width
        36 * @type {number}
        37 */
        38 this.width = width;
        39
        40 /**
        41 * Height
        42 * @type {number}
        43 */
        44 this.height = height;
        45};
        46
        47
        48/**
        49 * Compares sizes for equality.
        50 * @param {goog.math.Size} a A Size.
        51 * @param {goog.math.Size} b A Size.
        52 * @return {boolean} True iff the sizes have equal widths and equal
        53 * heights, or if both are null.
        54 */
        55goog.math.Size.equals = function(a, b) {
        56 if (a == b) {
        57 return true;
        58 }
        59 if (!a || !b) {
        60 return false;
        61 }
        62 return a.width == b.width && a.height == b.height;
        63};
        64
        65
        66/**
        67 * @return {!goog.math.Size} A new copy of the Size.
        68 */
        69goog.math.Size.prototype.clone = function() {
        70 return new goog.math.Size(this.width, this.height);
        71};
        72
        73
        74if (goog.DEBUG) {
        75 /**
        76 * Returns a nice string representing size.
        77 * @return {string} In the form (50 x 73).
        78 * @override
        79 */
        80 goog.math.Size.prototype.toString = function() {
        81 return '(' + this.width + ' x ' + this.height + ')';
        82 };
        83}
        84
        85
        86/**
        87 * @return {number} The longer of the two dimensions in the size.
        88 */
        89goog.math.Size.prototype.getLongest = function() {
        90 return Math.max(this.width, this.height);
        91};
        92
        93
        94/**
        95 * @return {number} The shorter of the two dimensions in the size.
        96 */
        97goog.math.Size.prototype.getShortest = function() {
        98 return Math.min(this.width, this.height);
        99};
        100
        101
        102/**
        103 * @return {number} The area of the size (width * height).
        104 */
        105goog.math.Size.prototype.area = function() {
        106 return this.width * this.height;
        107};
        108
        109
        110/**
        111 * @return {number} The perimeter of the size (width + height) * 2.
        112 */
        113goog.math.Size.prototype.perimeter = function() {
        114 return (this.width + this.height) * 2;
        115};
        116
        117
        118/**
        119 * @return {number} The ratio of the size's width to its height.
        120 */
        121goog.math.Size.prototype.aspectRatio = function() {
        122 return this.width / this.height;
        123};
        124
        125
        126/**
        127 * @return {boolean} True if the size has zero area, false if both dimensions
        128 * are non-zero numbers.
        129 */
        130goog.math.Size.prototype.isEmpty = function() {
        131 return !this.area();
        132};
        133
        134
        135/**
        136 * Clamps the width and height parameters upward to integer values.
        137 * @return {!goog.math.Size} This size with ceil'd components.
        138 */
        139goog.math.Size.prototype.ceil = function() {
        140 this.width = Math.ceil(this.width);
        141 this.height = Math.ceil(this.height);
        142 return this;
        143};
        144
        145
        146/**
        147 * @param {!goog.math.Size} target The target size.
        148 * @return {boolean} True if this Size is the same size or smaller than the
        149 * target size in both dimensions.
        150 */
        151goog.math.Size.prototype.fitsInside = function(target) {
        152 return this.width <= target.width && this.height <= target.height;
        153};
        154
        155
        156/**
        157 * Clamps the width and height parameters downward to integer values.
        158 * @return {!goog.math.Size} This size with floored components.
        159 */
        160goog.math.Size.prototype.floor = function() {
        161 this.width = Math.floor(this.width);
        162 this.height = Math.floor(this.height);
        163 return this;
        164};
        165
        166
        167/**
        168 * Rounds the width and height parameters to integer values.
        169 * @return {!goog.math.Size} This size with rounded components.
        170 */
        171goog.math.Size.prototype.round = function() {
        172 this.width = Math.round(this.width);
        173 this.height = Math.round(this.height);
        174 return this;
        175};
        176
        177
        178/**
        179 * Scales this size by the given scale factors. The width and height are scaled
        180 * by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy} is not
        181 * given, then {@code sx} is used for both the width and height.
        182 * @param {number} sx The scale factor to use for the width.
        183 * @param {number=} opt_sy The scale factor to use for the height.
        184 * @return {!goog.math.Size} This Size object after scaling.
        185 */
        186goog.math.Size.prototype.scale = function(sx, opt_sy) {
        187 var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
        188 this.width *= sx;
        189 this.height *= sy;
        190 return this;
        191};
        192
        193
        194/**
        195 * Uniformly scales the size to perfectly cover the dimensions of a given size.
        196 * If the size is already larger than the target, it will be scaled down to the
        197 * minimum size at which it still covers the entire target. The original aspect
        198 * ratio will be preserved.
        199 *
        200 * This function assumes that both Sizes contain strictly positive dimensions.
        201 * @param {!goog.math.Size} target The target size.
        202 * @return {!goog.math.Size} This Size object, after optional scaling.
        203 */
        204goog.math.Size.prototype.scaleToCover = function(target) {
        205 var s = this.aspectRatio() <= target.aspectRatio() ?
        206 target.width / this.width :
        207 target.height / this.height;
        208
        209 return this.scale(s);
        210};
        211
        212
        213/**
        214 * Uniformly scales the size to fit inside the dimensions of a given size. The
        215 * original aspect ratio will be preserved.
        216 *
        217 * This function assumes that both Sizes contain strictly positive dimensions.
        218 * @param {!goog.math.Size} target The target size.
        219 * @return {!goog.math.Size} This Size object, after optional scaling.
        220 */
        221goog.math.Size.prototype.scaleToFit = function(target) {
        222 var s = this.aspectRatio() > target.aspectRatio() ?
        223 target.width / this.width :
        224 target.height / this.height;
        225
        226 return this.scale(s);
        227};
        \ No newline at end of file diff --git a/docs/source/lib/goog/net/wrapperxmlhttpfactory.js.src.html b/docs/source/lib/goog/net/wrapperxmlhttpfactory.js.src.html index 000b805..ce7f2a4 100644 --- a/docs/source/lib/goog/net/wrapperxmlhttpfactory.js.src.html +++ b/docs/source/lib/goog/net/wrapperxmlhttpfactory.js.src.html @@ -1 +1 @@ -wrapperxmlhttpfactory.js

        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
        21goog.provide('goog.net.WrapperXmlHttpFactory');
        22
        23goog.require('goog.net.XmlHttpFactory');
        24
        25
        26
        27/**
        28 * An xhr factory subclass which can be constructed using two factory methods.
        29 * This exists partly to allow the preservation of goog.net.XmlHttp.setFactory()
        30 * with an unchanged signature.
        31 * @param {function() : !(XMLHttpRequest|GearsHttpRequest)} xhrFactory A
        32 * function which returns a new XHR object.
        33 * @param {function() : !Object} optionsFactory A function which returns the
        34 * options associated with xhr objects from this factory.
        35 * @extends {goog.net.XmlHttpFactory}
        36 * @constructor
        37 */
        38goog.net.WrapperXmlHttpFactory = function(xhrFactory, optionsFactory) {
        39 goog.net.XmlHttpFactory.call(this);
        40
        41 /**
        42 * XHR factory method.
        43 * @type {function() : !(XMLHttpRequest|GearsHttpRequest)}
        44 * @private
        45 */
        46 this.xhrFactory_ = xhrFactory;
        47
        48 /**
        49 * Options factory method.
        50 * @type {function() : !Object}
        51 * @private
        52 */
        53 this.optionsFactory_ = optionsFactory;
        54};
        55goog.inherits(goog.net.WrapperXmlHttpFactory, goog.net.XmlHttpFactory);
        56
        57
        58/** @override */
        59goog.net.WrapperXmlHttpFactory.prototype.createInstance = function() {
        60 return this.xhrFactory_();
        61};
        62
        63
        64/** @override */
        65goog.net.WrapperXmlHttpFactory.prototype.getOptions = function() {
        66 return this.optionsFactory_();
        67};
        68
        \ No newline at end of file +wrapperxmlhttpfactory.js

        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
        21goog.provide('goog.net.WrapperXmlHttpFactory');
        22
        23/** @suppress {extraRequire} Typedef. */
        24goog.require('goog.net.XhrLike');
        25goog.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 */
        41goog.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};
        58goog.inherits(goog.net.WrapperXmlHttpFactory, goog.net.XmlHttpFactory);
        59
        60
        61/** @override */
        62goog.net.WrapperXmlHttpFactory.prototype.createInstance = function() {
        63 return this.xhrFactory_();
        64};
        65
        66
        67/** @override */
        68goog.net.WrapperXmlHttpFactory.prototype.getOptions = function() {
        69 return this.optionsFactory_();
        70};
        71
        \ No newline at end of file diff --git a/docs/source/lib/goog/net/xhrlike.js.src.html b/docs/source/lib/goog/net/xhrlike.js.src.html new file mode 100644 index 0000000..39baf98 --- /dev/null +++ b/docs/source/lib/goog/net/xhrlike.js.src.html @@ -0,0 +1 @@ +xhrlike.js

        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
        15goog.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 */
        27goog.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 */
        34goog.net.XhrLike.OrNative;
        35
        36
        37/**
        38 * @type {function()|null|undefined}
        39 * @see http://www.w3.org/TR/XMLHttpRequest/#handler-xhr-onreadystatechange
        40 */
        41goog.net.XhrLike.prototype.onreadystatechange;
        42
        43
        44/**
        45 * @type {string}
        46 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute
        47 */
        48goog.net.XhrLike.prototype.responseText;
        49
        50
        51/**
        52 * @type {Document}
        53 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsexml-attribute
        54 */
        55goog.net.XhrLike.prototype.responseXML;
        56
        57
        58/**
        59 * @type {number}
        60 * @see http://www.w3.org/TR/XMLHttpRequest/#readystate
        61 */
        62goog.net.XhrLike.prototype.readyState;
        63
        64
        65/**
        66 * @type {number}
        67 * @see http://www.w3.org/TR/XMLHttpRequest/#status
        68 */
        69goog.net.XhrLike.prototype.status;
        70
        71
        72/**
        73 * @type {string}
        74 * @see http://www.w3.org/TR/XMLHttpRequest/#statustext
        75 */
        76goog.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 */
        87goog.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 */
        95goog.net.XhrLike.prototype.send = function(opt_data) {};
        96
        97
        98/**
        99 * @see http://www.w3.org/TR/XMLHttpRequest/#the-abort()-method
        100 */
        101goog.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 */
        109goog.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 */
        117goog.net.XhrLike.prototype.getResponseHeader = function(header) {};
        118
        119
        120/**
        121 * @return {string}
        122 * @see http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders()-method
        123 */
        124goog.net.XhrLike.prototype.getAllResponseHeaders = function() {};
        \ No newline at end of file diff --git a/docs/source/lib/goog/net/xmlhttp.js.src.html b/docs/source/lib/goog/net/xmlhttp.js.src.html index 9292738..52d0ce2 100644 --- a/docs/source/lib/goog/net/xmlhttp.js.src.html +++ b/docs/source/lib/goog/net/xmlhttp.js.src.html @@ -1 +1 @@ -xmlhttp.js

        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
        21goog.provide('goog.net.DefaultXmlHttpFactory');
        22goog.provide('goog.net.XmlHttp');
        23goog.provide('goog.net.XmlHttp.OptionType');
        24goog.provide('goog.net.XmlHttp.ReadyState');
        25
        26goog.require('goog.net.WrapperXmlHttpFactory');
        27goog.require('goog.net.XmlHttpFactory');
        28
        29
        30/**
        31 * Static class for creating XMLHttpRequest objects.
        32 * @return {!(XMLHttpRequest|GearsHttpRequest)} A new XMLHttpRequest object.
        33 */
        34goog.net.XmlHttp = function() {
        35 return goog.net.XmlHttp.factory_.createInstance();
        36};
        37
        38
        39/**
        40 * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to
        41 * true strips the ActiveX probing code.
        42 */
        43goog.define('goog.net.XmlHttp.ASSUME_NATIVE_XHR', false);
        44
        45
        46/**
        47 * Gets the options to use with the XMLHttpRequest objects obtained using
        48 * the static methods.
        49 * @return {Object} The options.
        50 */
        51goog.net.XmlHttp.getOptions = function() {
        52 return goog.net.XmlHttp.factory_.getOptions();
        53};
        54
        55
        56/**
        57 * Type of options that an XmlHttp object can have.
        58 * @enum {number}
        59 */
        60goog.net.XmlHttp.OptionType = {
        61 /**
        62 * Whether a goog.nullFunction should be used to clear the onreadystatechange
        63 * handler instead of null.
        64 */
        65 USE_NULL_FUNCTION: 0,
        66
        67 /**
        68 * NOTE(user): In IE if send() errors on a *local* request the readystate
        69 * is still changed to COMPLETE. We need to ignore it and allow the
        70 * try/catch around send() to pick up the error.
        71 */
        72 LOCAL_REQUEST_ERROR: 1
        73};
        74
        75
        76/**
        77 * Status constants for XMLHTTP, matches:
        78 * http://msdn.microsoft.com/library/default.asp?url=/library/
        79 * en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp
        80 * @enum {number}
        81 */
        82goog.net.XmlHttp.ReadyState = {
        83 /**
        84 * Constant for when xmlhttprequest.readyState is uninitialized
        85 */
        86 UNINITIALIZED: 0,
        87
        88 /**
        89 * Constant for when xmlhttprequest.readyState is loading.
        90 */
        91 LOADING: 1,
        92
        93 /**
        94 * Constant for when xmlhttprequest.readyState is loaded.
        95 */
        96 LOADED: 2,
        97
        98 /**
        99 * Constant for when xmlhttprequest.readyState is in an interactive state.
        100 */
        101 INTERACTIVE: 3,
        102
        103 /**
        104 * Constant for when xmlhttprequest.readyState is completed
        105 */
        106 COMPLETE: 4
        107};
        108
        109
        110/**
        111 * The global factory instance for creating XMLHttpRequest objects.
        112 * @type {goog.net.XmlHttpFactory}
        113 * @private
        114 */
        115goog.net.XmlHttp.factory_;
        116
        117
        118/**
        119 * Sets the factories for creating XMLHttpRequest objects and their options.
        120 * @param {Function} factory The factory for XMLHttpRequest objects.
        121 * @param {Function} optionsFactory The factory for options.
        122 * @deprecated Use setGlobalFactory instead.
        123 */
        124goog.net.XmlHttp.setFactory = function(factory, optionsFactory) {
        125 goog.net.XmlHttp.setGlobalFactory(new goog.net.WrapperXmlHttpFactory(
        126 /** @type {function() : !(XMLHttpRequest|GearsHttpRequest)} */ (factory),
        127 /** @type {function() : !Object}*/ (optionsFactory)));
        128};
        129
        130
        131/**
        132 * Sets the global factory object.
        133 * @param {!goog.net.XmlHttpFactory} factory New global factory object.
        134 */
        135goog.net.XmlHttp.setGlobalFactory = function(factory) {
        136 goog.net.XmlHttp.factory_ = factory;
        137};
        138
        139
        140
        141/**
        142 * Default factory to use when creating xhr objects. You probably shouldn't be
        143 * instantiating this directly, but rather using it via goog.net.XmlHttp.
        144 * @extends {goog.net.XmlHttpFactory}
        145 * @constructor
        146 */
        147goog.net.DefaultXmlHttpFactory = function() {
        148 goog.net.XmlHttpFactory.call(this);
        149};
        150goog.inherits(goog.net.DefaultXmlHttpFactory, goog.net.XmlHttpFactory);
        151
        152
        153/** @override */
        154goog.net.DefaultXmlHttpFactory.prototype.createInstance = function() {
        155 var progId = this.getProgId_();
        156 if (progId) {
        157 return new ActiveXObject(progId);
        158 } else {
        159 return new XMLHttpRequest();
        160 }
        161};
        162
        163
        164/** @override */
        165goog.net.DefaultXmlHttpFactory.prototype.internalGetOptions = function() {
        166 var progId = this.getProgId_();
        167 var options = {};
        168 if (progId) {
        169 options[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] = true;
        170 options[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] = true;
        171 }
        172 return options;
        173};
        174
        175
        176/**
        177 * The ActiveX PROG ID string to use to create xhr's in IE. Lazily initialized.
        178 * @type {string|undefined}
        179 * @private
        180 */
        181goog.net.DefaultXmlHttpFactory.prototype.ieProgId_;
        182
        183
        184/**
        185 * Initialize the private state used by other functions.
        186 * @return {string} The ActiveX PROG ID string to use to create xhr's in IE.
        187 * @private
        188 */
        189goog.net.DefaultXmlHttpFactory.prototype.getProgId_ = function() {
        190 if (goog.net.XmlHttp.ASSUME_NATIVE_XHR) {
        191 return '';
        192 }
        193
        194 // The following blog post describes what PROG IDs to use to create the
        195 // XMLHTTP object in Internet Explorer:
        196 // http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx
        197 // However we do not (yet) fully trust that this will be OK for old versions
        198 // of IE on Win9x so we therefore keep the last 2.
        199 if (!this.ieProgId_ && typeof XMLHttpRequest == 'undefined' &&
        200 typeof ActiveXObject != 'undefined') {
        201 // Candidate Active X types.
        202 var ACTIVE_X_IDENTS = ['MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.3.0',
        203 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP'];
        204 for (var i = 0; i < ACTIVE_X_IDENTS.length; i++) {
        205 var candidate = ACTIVE_X_IDENTS[i];
        206 /** @preserveTry */
        207 try {
        208 new ActiveXObject(candidate);
        209 // NOTE(user): cannot assign progid and return candidate in one line
        210 // because JSCompiler complaings: BUG 658126
        211 this.ieProgId_ = candidate;
        212 return candidate;
        213 } catch (e) {
        214 // do nothing; try next choice
        215 }
        216 }
        217
        218 // couldn't find any matches
        219 throw Error('Could not create ActiveXObject. ActiveX might be disabled,' +
        220 ' or MSXML might not be installed');
        221 }
        222
        223 return /** @type {string} */ (this.ieProgId_);
        224};
        225
        226
        227//Set the global factory to an instance of the default factory.
        228goog.net.XmlHttp.setGlobalFactory(new goog.net.DefaultXmlHttpFactory());
        \ No newline at end of file +xmlhttp.js

        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
        21goog.provide('goog.net.DefaultXmlHttpFactory');
        22goog.provide('goog.net.XmlHttp');
        23goog.provide('goog.net.XmlHttp.OptionType');
        24goog.provide('goog.net.XmlHttp.ReadyState');
        25goog.provide('goog.net.XmlHttpDefines');
        26
        27goog.require('goog.asserts');
        28goog.require('goog.net.WrapperXmlHttpFactory');
        29goog.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 */
        36goog.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(ruilopes): 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(ruilopes): Collapse both defines.
        48 */
        49goog.define('goog.net.XmlHttp.ASSUME_NATIVE_XHR', false);
        50
        51
        52/** @const */
        53goog.net.XmlHttpDefines = {};
        54
        55
        56/**
        57 * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to
        58 * true eliminates the ActiveX probing code.
        59 */
        60goog.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 */
        68goog.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 */
        77goog.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 */
        99goog.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 */
        132goog.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 */
        141goog.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 */
        152goog.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 */
        164goog.net.DefaultXmlHttpFactory = function() {
        165 goog.net.XmlHttpFactory.call(this);
        166};
        167goog.inherits(goog.net.DefaultXmlHttpFactory, goog.net.XmlHttpFactory);
        168
        169
        170/** @override */
        171goog.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 */
        182goog.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 */
        198goog.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 */
        206goog.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.
        246goog.net.XmlHttp.setGlobalFactory(new goog.net.DefaultXmlHttpFactory());
        \ No newline at end of file diff --git a/docs/source/lib/goog/net/xmlhttpfactory.js.src.html b/docs/source/lib/goog/net/xmlhttpfactory.js.src.html index 09406c3..6dde268 100644 --- a/docs/source/lib/goog/net/xmlhttpfactory.js.src.html +++ b/docs/source/lib/goog/net/xmlhttpfactory.js.src.html @@ -1 +1 @@ -xmlhttpfactory.js

        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
        21goog.provide('goog.net.XmlHttpFactory');
        22
        23
        24
        25/**
        26 * Abstract base class for an XmlHttpRequest factory.
        27 * @constructor
        28 */
        29goog.net.XmlHttpFactory = function() {
        30};
        31
        32
        33/**
        34 * Cache of options - we only actually call internalGetOptions once.
        35 * @type {Object}
        36 * @private
        37 */
        38goog.net.XmlHttpFactory.prototype.cachedOptions_ = null;
        39
        40
        41/**
        42 * @return {!(XMLHttpRequest|GearsHttpRequest)} A new XMLHttpRequest instance.
        43 */
        44goog.net.XmlHttpFactory.prototype.createInstance = goog.abstractMethod;
        45
        46
        47/**
        48 * @return {Object} Options describing how xhr objects obtained from this
        49 * factory should be used.
        50 */
        51goog.net.XmlHttpFactory.prototype.getOptions = function() {
        52 return this.cachedOptions_ ||
        53 (this.cachedOptions_ = this.internalGetOptions());
        54};
        55
        56
        57/**
        58 * Override this method in subclasses to preserve the caching offered by
        59 * getOptions().
        60 * @return {Object} Options describing how xhr objects obtained from this
        61 * factory should be used.
        62 * @protected
        63 */
        64goog.net.XmlHttpFactory.prototype.internalGetOptions = goog.abstractMethod;
        \ No newline at end of file +xmlhttpfactory.js

        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
        21goog.provide('goog.net.XmlHttpFactory');
        22
        23/** @suppress {extraRequire} Typedef. */
        24goog.require('goog.net.XhrLike');
        25
        26
        27
        28/**
        29 * Abstract base class for an XmlHttpRequest factory.
        30 * @constructor
        31 */
        32goog.net.XmlHttpFactory = function() {
        33};
        34
        35
        36/**
        37 * Cache of options - we only actually call internalGetOptions once.
        38 * @type {Object}
        39 * @private
        40 */
        41goog.net.XmlHttpFactory.prototype.cachedOptions_ = null;
        42
        43
        44/**
        45 * @return {!goog.net.XhrLike.OrNative} A new XhrLike instance.
        46 */
        47goog.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 */
        54goog.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 */
        67goog.net.XmlHttpFactory.prototype.internalGetOptions = goog.abstractMethod;
        \ No newline at end of file diff --git a/docs/source/lib/goog/object/object.js.src.html b/docs/source/lib/goog/object/object.js.src.html index a9b8f59..57141fb 100644 --- a/docs/source/lib/goog/object/object.js.src.html +++ b/docs/source/lib/goog/object/object.js.src.html @@ -1 +1 @@ -object.js

        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
        19goog.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 */
        32goog.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 */
        55goog.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 */
        80goog.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 */
        103goog.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 */
        127goog.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 */
        144goog.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 */
        164goog.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 */
        180goog.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 */
        196goog.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 */
        208goog.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 */
        224goog.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 */
        246goog.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 */
        269goog.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 */
        282goog.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 */
        304goog.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 */
        326goog.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 */
        338goog.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 */
        351goog.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 */
        365goog.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 */
        383goog.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 */
        401goog.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 */
        417goog.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 */
        431goog.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 */
        443goog.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 */
        470goog.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 */
        495goog.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 */
        509goog.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, {c: 2});
        529 * o; // {a: 0, b: 1, c: 2}
        530 *
        531 * @param {Object} target The object to modify.
        532 * @param {...Object} var_args The objects from which values will be copied.
        533 */
        534goog.object.extend = function(target, var_args) {
        535 var key, source;
        536 for (var i = 1; i < arguments.length; i++) {
        537 source = arguments[i];
        538 for (key in source) {
        539 target[key] = source[key];
        540 }
        541
        542 // For IE the for-in-loop does not contain any properties that are not
        543 // enumerable on the prototype object (for example isPrototypeOf from
        544 // Object.prototype) and it will also not include 'replace' on objects that
        545 // extend String and change 'replace' (not that it is common for anyone to
        546 // extend anything except Object).
        547
        548 for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
        549 key = goog.object.PROTOTYPE_FIELDS_[j];
        550 if (Object.prototype.hasOwnProperty.call(source, key)) {
        551 target[key] = source[key];
        552 }
        553 }
        554 }
        555};
        556
        557
        558/**
        559 * Creates a new object built from the key-value pairs provided as arguments.
        560 * @param {...*} var_args If only one argument is provided and it is an array
        561 * then this is used as the arguments, otherwise even arguments are used as
        562 * the property names and odd arguments are used as the property values.
        563 * @return {!Object} The new object.
        564 * @throws {Error} If there are uneven number of arguments or there is only one
        565 * non array argument.
        566 */
        567goog.object.create = function(var_args) {
        568 var argLength = arguments.length;
        569 if (argLength == 1 && goog.isArray(arguments[0])) {
        570 return goog.object.create.apply(null, arguments[0]);
        571 }
        572
        573 if (argLength % 2) {
        574 throw Error('Uneven number of arguments');
        575 }
        576
        577 var rv = {};
        578 for (var i = 0; i < argLength; i += 2) {
        579 rv[arguments[i]] = arguments[i + 1];
        580 }
        581 return rv;
        582};
        583
        584
        585/**
        586 * Creates a new object where the property names come from the arguments but
        587 * the value is always set to true
        588 * @param {...*} var_args If only one argument is provided and it is an array
        589 * then this is used as the arguments, otherwise the arguments are used
        590 * as the property names.
        591 * @return {!Object} The new object.
        592 */
        593goog.object.createSet = function(var_args) {
        594 var argLength = arguments.length;
        595 if (argLength == 1 && goog.isArray(arguments[0])) {
        596 return goog.object.createSet.apply(null, arguments[0]);
        597 }
        598
        599 var rv = {};
        600 for (var i = 0; i < argLength; i++) {
        601 rv[arguments[i]] = true;
        602 }
        603 return rv;
        604};
        605
        606
        607/**
        608 * Creates an immutable view of the underlying object, if the browser
        609 * supports immutable objects.
        610 *
        611 * In default mode, writes to this view will fail silently. In strict mode,
        612 * they will throw an error.
        613 *
        614 * @param {!Object.<K,V>} obj An object.
        615 * @return {!Object.<K,V>} An immutable view of that object, or the
        616 * original object if this browser does not support immutables.
        617 * @template K,V
        618 */
        619goog.object.createImmutableView = function(obj) {
        620 var result = obj;
        621 if (Object.isFrozen && !Object.isFrozen(obj)) {
        622 result = Object.create(obj);
        623 Object.freeze(result);
        624 }
        625 return result;
        626};
        627
        628
        629/**
        630 * @param {!Object} obj An object.
        631 * @return {boolean} Whether this is an immutable view of the object.
        632 */
        633goog.object.isImmutableView = function(obj) {
        634 return !!Object.isFrozen && Object.isFrozen(obj);
        635};
        \ No newline at end of file +object.js

        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 * @author arv@google.com (Erik Arvidsson)
        18 */
        19
        20goog.provide('goog.object');
        21
        22
        23/**
        24 * Calls a function for each element in an object/map/hash.
        25 *
        26 * @param {Object<K,V>} obj The object over which to iterate.
        27 * @param {function(this:T,V,?,Object<K,V>):?} f The function to call
        28 * for every element. This function takes 3 arguments (the value, the
        29 * key and the object) and the return value is ignored.
        30 * @param {T=} opt_obj This is used as the 'this' object within f.
        31 * @template T,K,V
        32 */
        33goog.object.forEach = function(obj, f, opt_obj) {
        34 for (var key in obj) {
        35 f.call(opt_obj, obj[key], key, obj);
        36 }
        37};
        38
        39
        40/**
        41 * Calls a function for each element in an object/map/hash. If that call returns
        42 * true, adds the element to a new object.
        43 *
        44 * @param {Object<K,V>} obj The object over which to iterate.
        45 * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call
        46 * for every element. This
        47 * function takes 3 arguments (the value, the key and the object)
        48 * and should return a boolean. If the return value is true the
        49 * element is added to the result object. If it is false the
        50 * element is not included.
        51 * @param {T=} opt_obj This is used as the 'this' object within f.
        52 * @return {!Object<K,V>} a new object in which only elements that passed the
        53 * test are present.
        54 * @template T,K,V
        55 */
        56goog.object.filter = function(obj, f, opt_obj) {
        57 var res = {};
        58 for (var key in obj) {
        59 if (f.call(opt_obj, obj[key], key, obj)) {
        60 res[key] = obj[key];
        61 }
        62 }
        63 return res;
        64};
        65
        66
        67/**
        68 * For every element in an object/map/hash calls a function and inserts the
        69 * result into a new object.
        70 *
        71 * @param {Object<K,V>} obj The object over which to iterate.
        72 * @param {function(this:T,V,?,Object<K,V>):R} f The function to call
        73 * for every element. This function
        74 * takes 3 arguments (the value, the key and the object)
        75 * and should return something. The result will be inserted
        76 * into a new object.
        77 * @param {T=} opt_obj This is used as the 'this' object within f.
        78 * @return {!Object<K,R>} a new object with the results from f.
        79 * @template T,K,V,R
        80 */
        81goog.object.map = function(obj, f, opt_obj) {
        82 var res = {};
        83 for (var key in obj) {
        84 res[key] = f.call(opt_obj, obj[key], key, obj);
        85 }
        86 return res;
        87};
        88
        89
        90/**
        91 * Calls a function for each element in an object/map/hash. If any
        92 * call returns true, returns true (without checking the rest). If
        93 * all calls return false, returns false.
        94 *
        95 * @param {Object<K,V>} obj The object to check.
        96 * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to
        97 * call for every element. This function
        98 * takes 3 arguments (the value, the key and the object) and should
        99 * return a boolean.
        100 * @param {T=} opt_obj This is used as the 'this' object within f.
        101 * @return {boolean} true if any element passes the test.
        102 * @template T,K,V
        103 */
        104goog.object.some = function(obj, f, opt_obj) {
        105 for (var key in obj) {
        106 if (f.call(opt_obj, obj[key], key, obj)) {
        107 return true;
        108 }
        109 }
        110 return false;
        111};
        112
        113
        114/**
        115 * Calls a function for each element in an object/map/hash. If
        116 * all calls return true, returns true. If any call returns false, returns
        117 * false at this point and does not continue to check the remaining elements.
        118 *
        119 * @param {Object<K,V>} obj The object to check.
        120 * @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to
        121 * call for every element. This function
        122 * takes 3 arguments (the value, the key and the object) and should
        123 * return a boolean.
        124 * @param {T=} opt_obj This is used as the 'this' object within f.
        125 * @return {boolean} false if any element fails the test.
        126 * @template T,K,V
        127 */
        128goog.object.every = function(obj, f, opt_obj) {
        129 for (var key in obj) {
        130 if (!f.call(opt_obj, obj[key], key, obj)) {
        131 return false;
        132 }
        133 }
        134 return true;
        135};
        136
        137
        138/**
        139 * Returns the number of key-value pairs in the object map.
        140 *
        141 * @param {Object} obj The object for which to get the number of key-value
        142 * pairs.
        143 * @return {number} The number of key-value pairs in the object map.
        144 */
        145goog.object.getCount = function(obj) {
        146 // JS1.5 has __count__ but it has been deprecated so it raises a warning...
        147 // in other words do not use. Also __count__ only includes the fields on the
        148 // actual object and not in the prototype chain.
        149 var rv = 0;
        150 for (var key in obj) {
        151 rv++;
        152 }
        153 return rv;
        154};
        155
        156
        157/**
        158 * Returns one key from the object map, if any exists.
        159 * For map literals the returned key will be the first one in most of the
        160 * browsers (a know exception is Konqueror).
        161 *
        162 * @param {Object} obj The object to pick a key from.
        163 * @return {string|undefined} The key or undefined if the object is empty.
        164 */
        165goog.object.getAnyKey = function(obj) {
        166 for (var key in obj) {
        167 return key;
        168 }
        169};
        170
        171
        172/**
        173 * Returns one value from the object map, if any exists.
        174 * For map literals the returned value will be the first one in most of the
        175 * browsers (a know exception is Konqueror).
        176 *
        177 * @param {Object<K,V>} obj The object to pick a value from.
        178 * @return {V|undefined} The value or undefined if the object is empty.
        179 * @template K,V
        180 */
        181goog.object.getAnyValue = function(obj) {
        182 for (var key in obj) {
        183 return obj[key];
        184 }
        185};
        186
        187
        188/**
        189 * Whether the object/hash/map contains the given object as a value.
        190 * An alias for goog.object.containsValue(obj, val).
        191 *
        192 * @param {Object<K,V>} obj The object in which to look for val.
        193 * @param {V} val The object for which to check.
        194 * @return {boolean} true if val is present.
        195 * @template K,V
        196 */
        197goog.object.contains = function(obj, val) {
        198 return goog.object.containsValue(obj, val);
        199};
        200
        201
        202/**
        203 * Returns the values of the object/map/hash.
        204 *
        205 * @param {Object<K,V>} obj The object from which to get the values.
        206 * @return {!Array<V>} The values in the object/map/hash.
        207 * @template K,V
        208 */
        209goog.object.getValues = function(obj) {
        210 var res = [];
        211 var i = 0;
        212 for (var key in obj) {
        213 res[i++] = obj[key];
        214 }
        215 return res;
        216};
        217
        218
        219/**
        220 * Returns the keys of the object/map/hash.
        221 *
        222 * @param {Object} obj The object from which to get the keys.
        223 * @return {!Array<string>} Array of property keys.
        224 */
        225goog.object.getKeys = function(obj) {
        226 var res = [];
        227 var i = 0;
        228 for (var key in obj) {
        229 res[i++] = key;
        230 }
        231 return res;
        232};
        233
        234
        235/**
        236 * Get a value from an object multiple levels deep. This is useful for
        237 * pulling values from deeply nested objects, such as JSON responses.
        238 * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
        239 *
        240 * @param {!Object} obj An object to get the value from. Can be array-like.
        241 * @param {...(string|number|!Array<number|string>)} var_args A number of keys
        242 * (as strings, or numbers, for array-like objects). Can also be
        243 * specified as a single array of keys.
        244 * @return {*} The resulting value. If, at any point, the value for a key
        245 * is undefined, returns undefined.
        246 */
        247goog.object.getValueByKeys = function(obj, var_args) {
        248 var isArrayLike = goog.isArrayLike(var_args);
        249 var keys = isArrayLike ? var_args : arguments;
        250
        251 // Start with the 2nd parameter for the variable parameters syntax.
        252 for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
        253 obj = obj[keys[i]];
        254 if (!goog.isDef(obj)) {
        255 break;
        256 }
        257 }
        258
        259 return obj;
        260};
        261
        262
        263/**
        264 * Whether the object/map/hash contains the given key.
        265 *
        266 * @param {Object} obj The object in which to look for key.
        267 * @param {?} key The key for which to check.
        268 * @return {boolean} true If the map contains the key.
        269 */
        270goog.object.containsKey = function(obj, key) {
        271 return obj !== null && key in obj;
        272};
        273
        274
        275/**
        276 * Whether the object/map/hash contains the given value. This is O(n).
        277 *
        278 * @param {Object<K,V>} obj The object in which to look for val.
        279 * @param {V} val The value for which to check.
        280 * @return {boolean} true If the map contains the value.
        281 * @template K,V
        282 */
        283goog.object.containsValue = function(obj, val) {
        284 for (var key in obj) {
        285 if (obj[key] == val) {
        286 return true;
        287 }
        288 }
        289 return false;
        290};
        291
        292
        293/**
        294 * Searches an object for an element that satisfies the given condition and
        295 * returns its key.
        296 * @param {Object<K,V>} obj The object to search in.
        297 * @param {function(this:T,V,string,Object<K,V>):boolean} f The
        298 * function to call for every element. Takes 3 arguments (the value,
        299 * the key and the object) and should return a boolean.
        300 * @param {T=} opt_this An optional "this" context for the function.
        301 * @return {string|undefined} The key of an element for which the function
        302 * returns true or undefined if no such element is found.
        303 * @template T,K,V
        304 */
        305goog.object.findKey = function(obj, f, opt_this) {
        306 for (var key in obj) {
        307 if (f.call(opt_this, obj[key], key, obj)) {
        308 return key;
        309 }
        310 }
        311 return undefined;
        312};
        313
        314
        315/**
        316 * Searches an object for an element that satisfies the given condition and
        317 * returns its value.
        318 * @param {Object<K,V>} obj The object to search in.
        319 * @param {function(this:T,V,string,Object<K,V>):boolean} f The function
        320 * to call for every element. Takes 3 arguments (the value, the key
        321 * and the object) and should return a boolean.
        322 * @param {T=} opt_this An optional "this" context for the function.
        323 * @return {V} The value of an element for which the function returns true or
        324 * undefined if no such element is found.
        325 * @template T,K,V
        326 */
        327goog.object.findValue = function(obj, f, opt_this) {
        328 var key = goog.object.findKey(obj, f, opt_this);
        329 return key && obj[key];
        330};
        331
        332
        333/**
        334 * Whether the object/map/hash is empty.
        335 *
        336 * @param {Object} obj The object to test.
        337 * @return {boolean} true if obj is empty.
        338 */
        339goog.object.isEmpty = function(obj) {
        340 for (var key in obj) {
        341 return false;
        342 }
        343 return true;
        344};
        345
        346
        347/**
        348 * Removes all key value pairs from the object/map/hash.
        349 *
        350 * @param {Object} obj The object to clear.
        351 */
        352goog.object.clear = function(obj) {
        353 for (var i in obj) {
        354 delete obj[i];
        355 }
        356};
        357
        358
        359/**
        360 * Removes a key-value pair based on the key.
        361 *
        362 * @param {Object} obj The object from which to remove the key.
        363 * @param {*} key The key to remove.
        364 * @return {boolean} Whether an element was removed.
        365 */
        366goog.object.remove = function(obj, key) {
        367 var rv;
        368 if (rv = key in /** @type {!Object} */ (obj)) {
        369 delete obj[key];
        370 }
        371 return rv;
        372};
        373
        374
        375/**
        376 * Adds a key-value pair to the object. Throws an exception if the key is
        377 * already in use. Use set if you want to change an existing pair.
        378 *
        379 * @param {Object<K,V>} obj The object to which to add the key-value pair.
        380 * @param {string} key The key to add.
        381 * @param {V} val The value to add.
        382 * @template K,V
        383 */
        384goog.object.add = function(obj, key, val) {
        385 if (obj !== null && key in obj) {
        386 throw Error('The object already contains the key "' + key + '"');
        387 }
        388 goog.object.set(obj, key, val);
        389};
        390
        391
        392/**
        393 * Returns the value for the given key.
        394 *
        395 * @param {Object<K,V>} obj The object from which to get the value.
        396 * @param {string} key The key for which to get the value.
        397 * @param {R=} opt_val The value to return if no item is found for the given
        398 * key (default is undefined).
        399 * @return {V|R|undefined} The value for the given key.
        400 * @template K,V,R
        401 */
        402goog.object.get = function(obj, key, opt_val) {
        403 if (obj !== null && key in obj) {
        404 return obj[key];
        405 }
        406 return opt_val;
        407};
        408
        409
        410/**
        411 * Adds a key-value pair to the object/map/hash.
        412 *
        413 * @param {Object<K,V>} obj The object to which to add the key-value pair.
        414 * @param {string} key The key to add.
        415 * @param {V} value The value to add.
        416 * @template K,V
        417 */
        418goog.object.set = function(obj, key, value) {
        419 obj[key] = value;
        420};
        421
        422
        423/**
        424 * Adds a key-value pair to the object/map/hash if it doesn't exist yet.
        425 *
        426 * @param {Object<K,V>} obj The object to which to add the key-value pair.
        427 * @param {string} key The key to add.
        428 * @param {V} value The value to add if the key wasn't present.
        429 * @return {V} The value of the entry at the end of the function.
        430 * @template K,V
        431 */
        432goog.object.setIfUndefined = function(obj, key, value) {
        433 return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value);
        434};
        435
        436
        437/**
        438 * Sets a key and value to an object if the key is not set. The value will be
        439 * the return value of the given function. If the key already exists, the
        440 * object will not be changed and the function will not be called (the function
        441 * will be lazily evaluated -- only called if necessary).
        442 *
        443 * This function is particularly useful for use with a map used a as a cache.
        444 *
        445 * @param {!Object<K,V>} obj The object to which to add the key-value pair.
        446 * @param {string} key The key to add.
        447 * @param {function():V} f The value to add if the key wasn't present.
        448 * @return {V} The value of the entry at the end of the function.
        449 * @template K,V
        450 */
        451goog.object.setWithReturnValueIfNotSet = function(obj, key, f) {
        452 if (key in obj) {
        453 return obj[key];
        454 }
        455
        456 var val = f();
        457 obj[key] = val;
        458 return val;
        459};
        460
        461
        462/**
        463 * Compares two objects for equality using === on the values.
        464 *
        465 * @param {!Object<K,V>} a
        466 * @param {!Object<K,V>} b
        467 * @return {boolean}
        468 * @template K,V
        469 */
        470goog.object.equals = function(a, b) {
        471 for (var k in a) {
        472 if (!(k in b) || a[k] !== b[k]) {
        473 return false;
        474 }
        475 }
        476 for (var k in b) {
        477 if (!(k in a)) {
        478 return false;
        479 }
        480 }
        481 return true;
        482};
        483
        484
        485/**
        486 * Does a flat clone of the object.
        487 *
        488 * @param {Object<K,V>} obj Object to clone.
        489 * @return {!Object<K,V>} Clone of the input object.
        490 * @template K,V
        491 */
        492goog.object.clone = function(obj) {
        493 // We cannot use the prototype trick because a lot of methods depend on where
        494 // the actual key is set.
        495
        496 var res = {};
        497 for (var key in obj) {
        498 res[key] = obj[key];
        499 }
        500 return res;
        501 // We could also use goog.mixin but I wanted this to be independent from that.
        502};
        503
        504
        505/**
        506 * Clones a value. The input may be an Object, Array, or basic type. Objects and
        507 * arrays will be cloned recursively.
        508 *
        509 * WARNINGS:
        510 * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects
        511 * that refer to themselves will cause infinite recursion.
        512 *
        513 * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and
        514 * copies UIDs created by <code>getUid</code> into cloned results.
        515 *
        516 * @param {*} obj The value to clone.
        517 * @return {*} A clone of the input value.
        518 */
        519goog.object.unsafeClone = function(obj) {
        520 var type = goog.typeOf(obj);
        521 if (type == 'object' || type == 'array') {
        522 if (goog.isFunction(obj.clone)) {
        523 return obj.clone();
        524 }
        525 var clone = type == 'array' ? [] : {};
        526 for (var key in obj) {
        527 clone[key] = goog.object.unsafeClone(obj[key]);
        528 }
        529 return clone;
        530 }
        531
        532 return obj;
        533};
        534
        535
        536/**
        537 * Returns a new object in which all the keys and values are interchanged
        538 * (keys become values and values become keys). If multiple keys map to the
        539 * same value, the chosen transposed value is implementation-dependent.
        540 *
        541 * @param {Object} obj The object to transpose.
        542 * @return {!Object} The transposed object.
        543 */
        544goog.object.transpose = function(obj) {
        545 var transposed = {};
        546 for (var key in obj) {
        547 transposed[obj[key]] = key;
        548 }
        549 return transposed;
        550};
        551
        552
        553/**
        554 * The names of the fields that are defined on Object.prototype.
        555 * @type {Array<string>}
        556 * @private
        557 */
        558goog.object.PROTOTYPE_FIELDS_ = [
        559 'constructor',
        560 'hasOwnProperty',
        561 'isPrototypeOf',
        562 'propertyIsEnumerable',
        563 'toLocaleString',
        564 'toString',
        565 'valueOf'
        566];
        567
        568
        569/**
        570 * Extends an object with another object.
        571 * This operates 'in-place'; it does not create a new Object.
        572 *
        573 * Example:
        574 * var o = {};
        575 * goog.object.extend(o, {a: 0, b: 1});
        576 * o; // {a: 0, b: 1}
        577 * goog.object.extend(o, {b: 2, c: 3});
        578 * o; // {a: 0, b: 2, c: 3}
        579 *
        580 * @param {Object} target The object to modify. Existing properties will be
        581 * overwritten if they are also present in one of the objects in
        582 * {@code var_args}.
        583 * @param {...Object} var_args The objects from which values will be copied.
        584 */
        585goog.object.extend = function(target, var_args) {
        586 var key, source;
        587 for (var i = 1; i < arguments.length; i++) {
        588 source = arguments[i];
        589 for (key in source) {
        590 target[key] = source[key];
        591 }
        592
        593 // For IE the for-in-loop does not contain any properties that are not
        594 // enumerable on the prototype object (for example isPrototypeOf from
        595 // Object.prototype) and it will also not include 'replace' on objects that
        596 // extend String and change 'replace' (not that it is common for anyone to
        597 // extend anything except Object).
        598
        599 for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
        600 key = goog.object.PROTOTYPE_FIELDS_[j];
        601 if (Object.prototype.hasOwnProperty.call(source, key)) {
        602 target[key] = source[key];
        603 }
        604 }
        605 }
        606};
        607
        608
        609/**
        610 * Creates a new object built from the key-value pairs provided as arguments.
        611 * @param {...*} var_args If only one argument is provided and it is an array
        612 * then this is used as the arguments, otherwise even arguments are used as
        613 * the property names and odd arguments are used as the property values.
        614 * @return {!Object} The new object.
        615 * @throws {Error} If there are uneven number of arguments or there is only one
        616 * non array argument.
        617 */
        618goog.object.create = function(var_args) {
        619 var argLength = arguments.length;
        620 if (argLength == 1 && goog.isArray(arguments[0])) {
        621 return goog.object.create.apply(null, arguments[0]);
        622 }
        623
        624 if (argLength % 2) {
        625 throw Error('Uneven number of arguments');
        626 }
        627
        628 var rv = {};
        629 for (var i = 0; i < argLength; i += 2) {
        630 rv[arguments[i]] = arguments[i + 1];
        631 }
        632 return rv;
        633};
        634
        635
        636/**
        637 * Creates a new object where the property names come from the arguments but
        638 * the value is always set to true
        639 * @param {...*} var_args If only one argument is provided and it is an array
        640 * then this is used as the arguments, otherwise the arguments are used
        641 * as the property names.
        642 * @return {!Object} The new object.
        643 */
        644goog.object.createSet = function(var_args) {
        645 var argLength = arguments.length;
        646 if (argLength == 1 && goog.isArray(arguments[0])) {
        647 return goog.object.createSet.apply(null, arguments[0]);
        648 }
        649
        650 var rv = {};
        651 for (var i = 0; i < argLength; i++) {
        652 rv[arguments[i]] = true;
        653 }
        654 return rv;
        655};
        656
        657
        658/**
        659 * Creates an immutable view of the underlying object, if the browser
        660 * supports immutable objects.
        661 *
        662 * In default mode, writes to this view will fail silently. In strict mode,
        663 * they will throw an error.
        664 *
        665 * @param {!Object<K,V>} obj An object.
        666 * @return {!Object<K,V>} An immutable view of that object, or the
        667 * original object if this browser does not support immutables.
        668 * @template K,V
        669 */
        670goog.object.createImmutableView = function(obj) {
        671 var result = obj;
        672 if (Object.isFrozen && !Object.isFrozen(obj)) {
        673 result = Object.create(obj);
        674 Object.freeze(result);
        675 }
        676 return result;
        677};
        678
        679
        680/**
        681 * @param {!Object} obj An object.
        682 * @return {boolean} Whether this is an immutable view of the object.
        683 */
        684goog.object.isImmutableView = function(obj) {
        685 return !!Object.isFrozen && Object.isFrozen(obj);
        686};
        \ No newline at end of file diff --git a/docs/source/lib/goog/promise/promise.js.src.html b/docs/source/lib/goog/promise/promise.js.src.html new file mode 100644 index 0000000..a2081c3 --- /dev/null +++ b/docs/source/lib/goog/promise/promise.js.src.html @@ -0,0 +1 @@ +promise.js

        lib/goog/promise/promise.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
        15goog.provide('goog.Promise');
        16
        17goog.require('goog.Thenable');
        18goog.require('goog.asserts');
        19goog.require('goog.async.FreeList');
        20goog.require('goog.async.run');
        21goog.require('goog.async.throwException');
        22goog.require('goog.debug.Error');
        23goog.require('goog.promise.Resolver');
        24
        25
        26
        27/**
        28 * Promises provide a result that may be resolved asynchronously. A Promise may
        29 * be resolved by being fulfilled with a fulfillment value, rejected with a
        30 * rejection reason, or blocked by another Promise. A Promise is said to be
        31 * settled if it is either fulfilled or rejected. Once settled, the Promise
        32 * result is immutable.
        33 *
        34 * Promises may represent results of any type, including undefined. Rejection
        35 * reasons are typically Errors, but may also be of any type. Closure Promises
        36 * allow for optional type annotations that enforce that fulfillment values are
        37 * of the appropriate types at compile time.
        38 *
        39 * The result of a Promise is accessible by calling {@code then} and registering
        40 * {@code onFulfilled} and {@code onRejected} callbacks. Once the Promise
        41 * is settled, the relevant callbacks are invoked with the fulfillment value or
        42 * rejection reason as argument. Callbacks are always invoked in the order they
        43 * were registered, even when additional {@code then} calls are made from inside
        44 * another callback. A callback is always run asynchronously sometime after the
        45 * scope containing the registering {@code then} invocation has returned.
        46 *
        47 * If a Promise is resolved with another Promise, the first Promise will block
        48 * until the second is settled, and then assumes the same result as the second
        49 * Promise. This allows Promises to depend on the results of other Promises,
        50 * linking together multiple asynchronous operations.
        51 *
        52 * This implementation is compatible with the Promises/A+ specification and
        53 * passes that specification's conformance test suite. A Closure Promise may be
        54 * resolved with a Promise instance (or sufficiently compatible Promise-like
        55 * object) created by other Promise implementations. From the specification,
        56 * Promise-like objects are known as "Thenables".
        57 *
        58 * @see http://promisesaplus.com/
        59 *
        60 * @param {function(
        61 * this:RESOLVER_CONTEXT,
        62 * function((TYPE|IThenable<TYPE>|Thenable)=),
        63 * function(*=)): void} resolver
        64 * Initialization function that is invoked immediately with {@code resolve}
        65 * and {@code reject} functions as arguments. The Promise is resolved or
        66 * rejected with the first argument passed to either function.
        67 * @param {RESOLVER_CONTEXT=} opt_context An optional context for executing the
        68 * resolver function. If unspecified, the resolver function will be executed
        69 * in the default scope.
        70 * @constructor
        71 * @struct
        72 * @final
        73 * @implements {goog.Thenable<TYPE>}
        74 * @template TYPE,RESOLVER_CONTEXT
        75 */
        76goog.Promise = function(resolver, opt_context) {
        77 /**
        78 * The internal state of this Promise. Either PENDING, FULFILLED, REJECTED, or
        79 * BLOCKED.
        80 * @private {goog.Promise.State_}
        81 */
        82 this.state_ = goog.Promise.State_.PENDING;
        83
        84 /**
        85 * The settled result of the Promise. Immutable once set with either a
        86 * fulfillment value or rejection reason.
        87 * @private {*}
        88 */
        89 this.result_ = undefined;
        90
        91 /**
        92 * For Promises created by calling {@code then()}, the originating parent.
        93 * @private {goog.Promise}
        94 */
        95 this.parent_ = null;
        96
        97 /**
        98 * The linked list of {@code onFulfilled} and {@code onRejected} callbacks
        99 * added to this Promise by calls to {@code then()}.
        100 * @private {?goog.Promise.CallbackEntry_}
        101 */
        102 this.callbackEntries_ = null;
        103
        104 /**
        105 * The tail of the linked list of {@code onFulfilled} and {@code onRejected}
        106 * callbacks added to this Promise by calls to {@code then()}.
        107 * @private {?goog.Promise.CallbackEntry_}
        108 */
        109 this.callbackEntriesTail_ = null;
        110
        111 /**
        112 * Whether the Promise is in the queue of Promises to execute.
        113 * @private {boolean}
        114 */
        115 this.executing_ = false;
        116
        117 if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
        118 /**
        119 * A timeout ID used when the {@code UNHANDLED_REJECTION_DELAY} is greater
        120 * than 0 milliseconds. The ID is set when the Promise is rejected, and
        121 * cleared only if an {@code onRejected} callback is invoked for the
        122 * Promise (or one of its descendants) before the delay is exceeded.
        123 *
        124 * If the rejection is not handled before the timeout completes, the
        125 * rejection reason is passed to the unhandled rejection handler.
        126 * @private {number}
        127 */
        128 this.unhandledRejectionId_ = 0;
        129 } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
        130 /**
        131 * When the {@code UNHANDLED_REJECTION_DELAY} is set to 0 milliseconds, a
        132 * boolean that is set if the Promise is rejected, and reset to false if an
        133 * {@code onRejected} callback is invoked for the Promise (or one of its
        134 * descendants). If the rejection is not handled before the next timestep,
        135 * the rejection reason is passed to the unhandled rejection handler.
        136 * @private {boolean}
        137 */
        138 this.hadUnhandledRejection_ = false;
        139 }
        140
        141 if (goog.Promise.LONG_STACK_TRACES) {
        142 /**
        143 * A list of stack trace frames pointing to the locations where this Promise
        144 * was created or had callbacks added to it. Saved to add additional context
        145 * to stack traces when an exception is thrown.
        146 * @private {!Array<string>}
        147 */
        148 this.stack_ = [];
        149 this.addStackTrace_(new Error('created'));
        150
        151 /**
        152 * Index of the most recently executed stack frame entry.
        153 * @private {number}
        154 */
        155 this.currentStep_ = 0;
        156 }
        157
        158 // As an optimization, we can skip this if resolver is goog.nullFunction.
        159 // This value is passed internally when creating a promise which will be
        160 // resolved through a more optimized path.
        161 if (resolver != goog.nullFunction) {
        162 try {
        163 var self = this;
        164 resolver.call(
        165 opt_context,
        166 function(value) {
        167 self.resolve_(goog.Promise.State_.FULFILLED, value);
        168 },
        169 function(reason) {
        170 if (goog.DEBUG &&
        171 !(reason instanceof goog.Promise.CancellationError)) {
        172 try {
        173 // Promise was rejected. Step up one call frame to see why.
        174 if (reason instanceof Error) {
        175 throw reason;
        176 } else {
        177 throw new Error('Promise rejected.');
        178 }
        179 } catch (e) {
        180 // Only thrown so browser dev tools can catch rejections of
        181 // promises when the option to break on caught exceptions is
        182 // activated.
        183 }
        184 }
        185 self.resolve_(goog.Promise.State_.REJECTED, reason);
        186 });
        187 } catch (e) {
        188 this.resolve_(goog.Promise.State_.REJECTED, e);
        189 }
        190 }
        191};
        192
        193
        194/**
        195 * @define {boolean} Whether traces of {@code then} calls should be included in
        196 * exceptions thrown
        197 */
        198goog.define('goog.Promise.LONG_STACK_TRACES', false);
        199
        200
        201/**
        202 * @define {number} The delay in milliseconds before a rejected Promise's reason
        203 * is passed to the rejection handler. By default, the rejection handler
        204 * rethrows the rejection reason so that it appears in the developer console or
        205 * {@code window.onerror} handler.
        206 *
        207 * Rejections are rethrown as quickly as possible by default. A negative value
        208 * disables rejection handling entirely.
        209 */
        210goog.define('goog.Promise.UNHANDLED_REJECTION_DELAY', 0);
        211
        212
        213/**
        214 * The possible internal states for a Promise. These states are not directly
        215 * observable to external callers.
        216 * @enum {number}
        217 * @private
        218 */
        219goog.Promise.State_ = {
        220 /** The Promise is waiting for resolution. */
        221 PENDING: 0,
        222
        223 /** The Promise is blocked waiting for the result of another Thenable. */
        224 BLOCKED: 1,
        225
        226 /** The Promise has been resolved with a fulfillment value. */
        227 FULFILLED: 2,
        228
        229 /** The Promise has been resolved with a rejection reason. */
        230 REJECTED: 3
        231};
        232
        233
        234
        235/**
        236 * Entries in the callback chain. Each call to {@code then},
        237 * {@code thenCatch}, or {@code thenAlways} creates an entry containing the
        238 * functions that may be invoked once the Promise is settled.
        239 *
        240 * @private @final @struct @constructor
        241 */
        242goog.Promise.CallbackEntry_ = function() {
        243 /** @type {?goog.Promise} */
        244 this.child = null;
        245 /** @type {Function} */
        246 this.onFulfilled = null;
        247 /** @type {Function} */
        248 this.onRejected = null;
        249 /** @type {?} */
        250 this.context = null;
        251 /** @type {?goog.Promise.CallbackEntry_} */
        252 this.next = null;
        253
        254 /**
        255 * A boolean value to indicate this is a "thenAlways" callback entry.
        256 * Unlike a normal "then/thenVoid" a "thenAlways doesn't participate
        257 * in "cancel" considerations but is simply an observer and requires
        258 * special handling.
        259 * @type {boolean}
        260 */
        261 this.always = false;
        262};
        263
        264
        265/** clear the object prior to reuse */
        266goog.Promise.CallbackEntry_.prototype.reset = function() {
        267 this.child = null;
        268 this.onFulfilled = null;
        269 this.onRejected = null;
        270 this.context = null;
        271 this.always = false;
        272};
        273
        274
        275/**
        276 * @define {number} The number of currently unused objects to keep around for
        277 * reuse.
        278 */
        279goog.define('goog.Promise.DEFAULT_MAX_UNUSED', 100);
        280
        281
        282/** @const @private {goog.async.FreeList<!goog.Promise.CallbackEntry_>} */
        283goog.Promise.freelist_ = new goog.async.FreeList(
        284 function() {
        285 return new goog.Promise.CallbackEntry_();
        286 },
        287 function(item) {
        288 item.reset();
        289 },
        290 goog.Promise.DEFAULT_MAX_UNUSED);
        291
        292
        293/**
        294 * @param {Function} onFulfilled
        295 * @param {Function} onRejected
        296 * @param {?} context
        297 * @return {!goog.Promise.CallbackEntry_}
        298 * @private
        299 */
        300goog.Promise.getCallbackEntry_ = function(onFulfilled, onRejected, context) {
        301 var entry = goog.Promise.freelist_.get();
        302 entry.onFulfilled = onFulfilled;
        303 entry.onRejected = onRejected;
        304 entry.context = context;
        305 return entry;
        306};
        307
        308
        309/**
        310 * @param {!goog.Promise.CallbackEntry_} entry
        311 * @private
        312 */
        313goog.Promise.returnEntry_ = function(entry) {
        314 goog.Promise.freelist_.put(entry);
        315};
        316
        317
        318// NOTE: this is the same template expression as is used for
        319// goog.IThenable.prototype.then
        320
        321
        322/**
        323 * @param {VALUE=} opt_value
        324 * @return {RESULT} A new Promise that is immediately resolved
        325 * with the given value. If the input value is already a goog.Promise, it
        326 * will be returned immediately without creating a new instance.
        327 * @template VALUE
        328 * @template RESULT := type('goog.Promise',
        329 * cond(isUnknown(VALUE), unknown(),
        330 * mapunion(VALUE, (V) =>
        331 * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),
        332 * templateTypeOf(V, 0),
        333 * cond(sub(V, 'Thenable'),
        334 * unknown(),
        335 * V)))))
        336 * =:
        337 */
        338goog.Promise.resolve = function(opt_value) {
        339 if (opt_value instanceof goog.Promise) {
        340 // Avoid creating a new object if we already have a promise object
        341 // of the correct type.
        342 return opt_value;
        343 }
        344
        345 // Passing goog.nullFunction will cause the constructor to take an optimized
        346 // path that skips calling the resolver function.
        347 var promise = new goog.Promise(goog.nullFunction);
        348 promise.resolve_(goog.Promise.State_.FULFILLED, opt_value);
        349 return promise;
        350};
        351
        352
        353/**
        354 * @param {*=} opt_reason
        355 * @return {!goog.Promise} A new Promise that is immediately rejected with the
        356 * given reason.
        357 */
        358goog.Promise.reject = function(opt_reason) {
        359 return new goog.Promise(function(resolve, reject) {
        360 reject(opt_reason);
        361 });
        362};
        363
        364
        365/**
        366 * This is identical to
        367 * {@code goog.Promise.resolve(value).then(onFulfilled, onRejected)}, but it
        368 * avoids creating an unnecessary wrapper Promise when {@code value} is already
        369 * thenable.
        370 *
        371 * @param {?(goog.Thenable<TYPE>|Thenable|TYPE)} value
        372 * @param {function(TYPE): ?} onFulfilled
        373 * @param {function(*): *} onRejected
        374 * @template TYPE
        375 * @private
        376 */
        377goog.Promise.resolveThen_ = function(value, onFulfilled, onRejected) {
        378 var isThenable = goog.Promise.maybeThen_(
        379 value, onFulfilled, onRejected, null);
        380 if (!isThenable) {
        381 goog.async.run(goog.partial(onFulfilled, value));
        382 }
        383};
        384
        385
        386/**
        387 * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}
        388 * promises
        389 * @return {!goog.Promise<TYPE>} A Promise that receives the result of the
        390 * first Promise (or Promise-like) input to settle immediately after it
        391 * settles.
        392 * @template TYPE
        393 */
        394goog.Promise.race = function(promises) {
        395 return new goog.Promise(function(resolve, reject) {
        396 if (!promises.length) {
        397 resolve(undefined);
        398 }
        399 for (var i = 0, promise; i < promises.length; i++) {
        400 promise = promises[i];
        401 goog.Promise.resolveThen_(promise, resolve, reject);
        402 }
        403 });
        404};
        405
        406
        407/**
        408 * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}
        409 * promises
        410 * @return {!goog.Promise<!Array<TYPE>>} A Promise that receives a list of
        411 * every fulfilled value once every input Promise (or Promise-like) is
        412 * successfully fulfilled, or is rejected with the first rejection reason
        413 * immediately after it is rejected.
        414 * @template TYPE
        415 */
        416goog.Promise.all = function(promises) {
        417 return new goog.Promise(function(resolve, reject) {
        418 var toFulfill = promises.length;
        419 var values = [];
        420
        421 if (!toFulfill) {
        422 resolve(values);
        423 return;
        424 }
        425
        426 var onFulfill = function(index, value) {
        427 toFulfill--;
        428 values[index] = value;
        429 if (toFulfill == 0) {
        430 resolve(values);
        431 }
        432 };
        433
        434 var onReject = function(reason) {
        435 reject(reason);
        436 };
        437
        438 for (var i = 0, promise; i < promises.length; i++) {
        439 promise = promises[i];
        440 goog.Promise.resolveThen_(
        441 promise, goog.partial(onFulfill, i), onReject);
        442 }
        443 });
        444};
        445
        446
        447/**
        448 * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}
        449 * promises
        450 * @return {!goog.Promise<!Array<{
        451 * fulfilled: boolean,
        452 * value: (TYPE|undefined),
        453 * reason: (*|undefined)}>>} A Promise that resolves with a list of
        454 * result objects once all input Promises (or Promise-like) have
        455 * settled. Each result object contains a 'fulfilled' boolean indicating
        456 * whether an input Promise was fulfilled or rejected. For fulfilled
        457 * Promises, the resulting value is stored in the 'value' field. For
        458 * rejected Promises, the rejection reason is stored in the 'reason'
        459 * field.
        460 * @template TYPE
        461 */
        462goog.Promise.allSettled = function(promises) {
        463 return new goog.Promise(function(resolve, reject) {
        464 var toSettle = promises.length;
        465 var results = [];
        466
        467 if (!toSettle) {
        468 resolve(results);
        469 return;
        470 }
        471
        472 var onSettled = function(index, fulfilled, result) {
        473 toSettle--;
        474 results[index] = fulfilled ?
        475 {fulfilled: true, value: result} :
        476 {fulfilled: false, reason: result};
        477 if (toSettle == 0) {
        478 resolve(results);
        479 }
        480 };
        481
        482 for (var i = 0, promise; i < promises.length; i++) {
        483 promise = promises[i];
        484 goog.Promise.resolveThen_(promise,
        485 goog.partial(onSettled, i, true /* fulfilled */),
        486 goog.partial(onSettled, i, false /* fulfilled */));
        487 }
        488 });
        489};
        490
        491
        492/**
        493 * @param {!Array<?(goog.Promise<TYPE>|goog.Thenable<TYPE>|Thenable|*)>}
        494 * promises
        495 * @return {!goog.Promise<TYPE>} A Promise that receives the value of the first
        496 * input to be fulfilled, or is rejected with a list of every rejection
        497 * reason if all inputs are rejected.
        498 * @template TYPE
        499 */
        500goog.Promise.firstFulfilled = function(promises) {
        501 return new goog.Promise(function(resolve, reject) {
        502 var toReject = promises.length;
        503 var reasons = [];
        504
        505 if (!toReject) {
        506 resolve(undefined);
        507 return;
        508 }
        509
        510 var onFulfill = function(value) {
        511 resolve(value);
        512 };
        513
        514 var onReject = function(index, reason) {
        515 toReject--;
        516 reasons[index] = reason;
        517 if (toReject == 0) {
        518 reject(reasons);
        519 }
        520 };
        521
        522 for (var i = 0, promise; i < promises.length; i++) {
        523 promise = promises[i];
        524 goog.Promise.resolveThen_(
        525 promise, onFulfill, goog.partial(onReject, i));
        526 }
        527 });
        528};
        529
        530
        531/**
        532 * @return {!goog.promise.Resolver<TYPE>} Resolver wrapping the promise and its
        533 * resolve / reject functions. Resolving or rejecting the resolver
        534 * resolves or rejects the promise.
        535 * @template TYPE
        536 */
        537goog.Promise.withResolver = function() {
        538 var resolve, reject;
        539 var promise = new goog.Promise(function(rs, rj) {
        540 resolve = rs;
        541 reject = rj;
        542 });
        543 return new goog.Promise.Resolver_(promise, resolve, reject);
        544};
        545
        546
        547/**
        548 * Adds callbacks that will operate on the result of the Promise, returning a
        549 * new child Promise.
        550 *
        551 * If the Promise is fulfilled, the {@code onFulfilled} callback will be invoked
        552 * with the fulfillment value as argument, and the child Promise will be
        553 * fulfilled with the return value of the callback. If the callback throws an
        554 * exception, the child Promise will be rejected with the thrown value instead.
        555 *
        556 * If the Promise is rejected, the {@code onRejected} callback will be invoked
        557 * with the rejection reason as argument, and the child Promise will be resolved
        558 * with the return value or rejected with the thrown value of the callback.
        559 *
        560 * @override
        561 */
        562goog.Promise.prototype.then = function(
        563 opt_onFulfilled, opt_onRejected, opt_context) {
        564
        565 if (opt_onFulfilled != null) {
        566 goog.asserts.assertFunction(opt_onFulfilled,
        567 'opt_onFulfilled should be a function.');
        568 }
        569 if (opt_onRejected != null) {
        570 goog.asserts.assertFunction(opt_onRejected,
        571 'opt_onRejected should be a function. Did you pass opt_context ' +
        572 'as the second argument instead of the third?');
        573 }
        574
        575 if (goog.Promise.LONG_STACK_TRACES) {
        576 this.addStackTrace_(new Error('then'));
        577 }
        578
        579 return this.addChildPromise_(
        580 goog.isFunction(opt_onFulfilled) ? opt_onFulfilled : null,
        581 goog.isFunction(opt_onRejected) ? opt_onRejected : null,
        582 opt_context);
        583};
        584goog.Thenable.addImplementation(goog.Promise);
        585
        586
        587/**
        588 * Adds callbacks that will operate on the result of the Promise without
        589 * returning a child Promise (unlike "then").
        590 *
        591 * If the Promise is fulfilled, the {@code onFulfilled} callback will be invoked
        592 * with the fulfillment value as argument.
        593 *
        594 * If the Promise is rejected, the {@code onRejected} callback will be invoked
        595 * with the rejection reason as argument.
        596 *
        597 * @param {?(function(this:THIS, TYPE):?)=} opt_onFulfilled A
        598 * function that will be invoked with the fulfillment value if the Promise
        599 * is fulfilled.
        600 * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will
        601 * be invoked with the rejection reason if the Promise is rejected.
        602 * @param {THIS=} opt_context An optional context object that will be the
        603 * execution context for the callbacks. By default, functions are executed
        604 * with the default this.
        605 * @package
        606 * @template THIS
        607 */
        608goog.Promise.prototype.thenVoid = function(
        609 opt_onFulfilled, opt_onRejected, opt_context) {
        610
        611 if (opt_onFulfilled != null) {
        612 goog.asserts.assertFunction(opt_onFulfilled,
        613 'opt_onFulfilled should be a function.');
        614 }
        615 if (opt_onRejected != null) {
        616 goog.asserts.assertFunction(opt_onRejected,
        617 'opt_onRejected should be a function. Did you pass opt_context ' +
        618 'as the second argument instead of the third?');
        619 }
        620
        621 if (goog.Promise.LONG_STACK_TRACES) {
        622 this.addStackTrace_(new Error('then'));
        623 }
        624
        625 // Note: no default rejection handler is provided here as we need to
        626 // distinguish unhandled rejections.
        627 this.addCallbackEntry_(goog.Promise.getCallbackEntry_(
        628 opt_onFulfilled || goog.nullFunction,
        629 opt_onRejected || null,
        630 opt_context));
        631};
        632
        633
        634/**
        635 * Adds a callback that will be invoked when the Promise is settled (fulfilled
        636 * or rejected). The callback receives no argument, and no new child Promise is
        637 * created. This is useful for ensuring that cleanup takes place after certain
        638 * asynchronous operations. Callbacks added with {@code thenAlways} will be
        639 * executed in the same order with other calls to {@code then},
        640 * {@code thenAlways}, or {@code thenCatch}.
        641 *
        642 * Since it does not produce a new child Promise, cancellation propagation is
        643 * not prevented by adding callbacks with {@code thenAlways}. A Promise that has
        644 * a cleanup handler added with {@code thenAlways} will be canceled if all of
        645 * its children created by {@code then} (or {@code thenCatch}) are canceled.
        646 * Additionally, since any rejections are not passed to the callback, it does
        647 * not stop the unhandled rejection handler from running.
        648 *
        649 * @param {function(this:THIS): void} onSettled A function that will be invoked
        650 * when the Promise is settled (fulfilled or rejected).
        651 * @param {THIS=} opt_context An optional context object that will be the
        652 * execution context for the callbacks. By default, functions are executed
        653 * in the global scope.
        654 * @return {!goog.Promise<TYPE>} This Promise, for chaining additional calls.
        655 * @template THIS
        656 */
        657goog.Promise.prototype.thenAlways = function(onSettled, opt_context) {
        658 if (goog.Promise.LONG_STACK_TRACES) {
        659 this.addStackTrace_(new Error('thenAlways'));
        660 }
        661
        662 var entry = goog.Promise.getCallbackEntry_(onSettled, onSettled, opt_context);
        663 entry.always = true;
        664 this.addCallbackEntry_(entry);
        665 return this;
        666};
        667
        668
        669/**
        670 * Adds a callback that will be invoked only if the Promise is rejected. This
        671 * is equivalent to {@code then(null, onRejected)}.
        672 *
        673 * @param {!function(this:THIS, *): *} onRejected A function that will be
        674 * invoked with the rejection reason if the Promise is rejected.
        675 * @param {THIS=} opt_context An optional context object that will be the
        676 * execution context for the callbacks. By default, functions are executed
        677 * in the global scope.
        678 * @return {!goog.Promise} A new Promise that will receive the result of the
        679 * callback.
        680 * @template THIS
        681 */
        682goog.Promise.prototype.thenCatch = function(onRejected, opt_context) {
        683 if (goog.Promise.LONG_STACK_TRACES) {
        684 this.addStackTrace_(new Error('thenCatch'));
        685 }
        686 return this.addChildPromise_(null, onRejected, opt_context);
        687};
        688
        689
        690/**
        691 * Cancels the Promise if it is still pending by rejecting it with a cancel
        692 * Error. No action is performed if the Promise is already resolved.
        693 *
        694 * All child Promises of the canceled Promise will be rejected with the same
        695 * cancel error, as with normal Promise rejection. If the Promise to be canceled
        696 * is the only child of a pending Promise, the parent Promise will also be
        697 * canceled. Cancellation may propagate upward through multiple generations.
        698 *
        699 * @param {string=} opt_message An optional debugging message for describing the
        700 * cancellation reason.
        701 */
        702goog.Promise.prototype.cancel = function(opt_message) {
        703 if (this.state_ == goog.Promise.State_.PENDING) {
        704 goog.async.run(function() {
        705 var err = new goog.Promise.CancellationError(opt_message);
        706 this.cancelInternal_(err);
        707 }, this);
        708 }
        709};
        710
        711
        712/**
        713 * Cancels this Promise with the given error.
        714 *
        715 * @param {!Error} err The cancellation error.
        716 * @private
        717 */
        718goog.Promise.prototype.cancelInternal_ = function(err) {
        719 if (this.state_ == goog.Promise.State_.PENDING) {
        720 if (this.parent_) {
        721 // Cancel the Promise and remove it from the parent's child list.
        722 this.parent_.cancelChild_(this, err);
        723 this.parent_ = null;
        724 } else {
        725 this.resolve_(goog.Promise.State_.REJECTED, err);
        726 }
        727 }
        728};
        729
        730
        731/**
        732 * Cancels a child Promise from the list of callback entries. If the Promise has
        733 * not already been resolved, reject it with a cancel error. If there are no
        734 * other children in the list of callback entries, propagate the cancellation
        735 * by canceling this Promise as well.
        736 *
        737 * @param {!goog.Promise} childPromise The Promise to cancel.
        738 * @param {!Error} err The cancel error to use for rejecting the Promise.
        739 * @private
        740 */
        741goog.Promise.prototype.cancelChild_ = function(childPromise, err) {
        742 if (!this.callbackEntries_) {
        743 return;
        744 }
        745 var childCount = 0;
        746 var childEntry = null;
        747 var beforeChildEntry = null;
        748
        749 // Find the callback entry for the childPromise, and count whether there are
        750 // additional child Promises.
        751 for (var entry = this.callbackEntries_; entry; entry = entry.next) {
        752 if (!entry.always) {
        753 childCount++;
        754 if (entry.child == childPromise) {
        755 childEntry = entry;
        756 }
        757 if (childEntry && childCount > 1) {
        758 break;
        759 }
        760 }
        761 if (!childEntry) {
        762 beforeChildEntry = entry;
        763 }
        764 }
        765
        766 // Can a child entry be missing?
        767
        768 // If the child Promise was the only child, cancel this Promise as well.
        769 // Otherwise, reject only the child Promise with the cancel error.
        770 if (childEntry) {
        771 if (this.state_ == goog.Promise.State_.PENDING && childCount == 1) {
        772 this.cancelInternal_(err);
        773 } else {
        774 if (beforeChildEntry) {
        775 this.removeEntryAfter_(beforeChildEntry);
        776 } else {
        777 this.popEntry_();
        778 }
        779
        780 this.executeCallback_(
        781 childEntry, goog.Promise.State_.REJECTED, err);
        782 }
        783 }
        784};
        785
        786
        787/**
        788 * Adds a callback entry to the current Promise, and schedules callback
        789 * execution if the Promise has already been settled.
        790 *
        791 * @param {goog.Promise.CallbackEntry_} callbackEntry Record containing
        792 * {@code onFulfilled} and {@code onRejected} callbacks to execute after
        793 * the Promise is settled.
        794 * @private
        795 */
        796goog.Promise.prototype.addCallbackEntry_ = function(callbackEntry) {
        797 if (!this.hasEntry_() &&
        798 (this.state_ == goog.Promise.State_.FULFILLED ||
        799 this.state_ == goog.Promise.State_.REJECTED)) {
        800 this.scheduleCallbacks_();
        801 }
        802 this.queueEntry_(callbackEntry);
        803};
        804
        805
        806/**
        807 * Creates a child Promise and adds it to the callback entry list. The result of
        808 * the child Promise is determined by the state of the parent Promise and the
        809 * result of the {@code onFulfilled} or {@code onRejected} callbacks as
        810 * specified in the Promise resolution procedure.
        811 *
        812 * @see http://promisesaplus.com/#the__method
        813 *
        814 * @param {?function(this:THIS, TYPE):
        815 * (RESULT|goog.Promise<RESULT>|Thenable)} onFulfilled A callback that
        816 * will be invoked if the Promise is fullfilled, or null.
        817 * @param {?function(this:THIS, *): *} onRejected A callback that will be
        818 * invoked if the Promise is rejected, or null.
        819 * @param {THIS=} opt_context An optional execution context for the callbacks.
        820 * in the default calling context.
        821 * @return {!goog.Promise} The child Promise.
        822 * @template RESULT,THIS
        823 * @private
        824 */
        825goog.Promise.prototype.addChildPromise_ = function(
        826 onFulfilled, onRejected, opt_context) {
        827
        828 /** @type {goog.Promise.CallbackEntry_} */
        829 var callbackEntry = goog.Promise.getCallbackEntry_(null, null, null);
        830
        831 callbackEntry.child = new goog.Promise(function(resolve, reject) {
        832 // Invoke onFulfilled, or resolve with the parent's value if absent.
        833 callbackEntry.onFulfilled = onFulfilled ? function(value) {
        834 try {
        835 var result = onFulfilled.call(opt_context, value);
        836 resolve(result);
        837 } catch (err) {
        838 reject(err);
        839 }
        840 } : resolve;
        841
        842 // Invoke onRejected, or reject with the parent's reason if absent.
        843 callbackEntry.onRejected = onRejected ? function(reason) {
        844 try {
        845 var result = onRejected.call(opt_context, reason);
        846 if (!goog.isDef(result) &&
        847 reason instanceof goog.Promise.CancellationError) {
        848 // Propagate cancellation to children if no other result is returned.
        849 reject(reason);
        850 } else {
        851 resolve(result);
        852 }
        853 } catch (err) {
        854 reject(err);
        855 }
        856 } : reject;
        857 });
        858
        859 callbackEntry.child.parent_ = this;
        860 this.addCallbackEntry_(callbackEntry);
        861 return callbackEntry.child;
        862};
        863
        864
        865/**
        866 * Unblocks the Promise and fulfills it with the given value.
        867 *
        868 * @param {TYPE} value
        869 * @private
        870 */
        871goog.Promise.prototype.unblockAndFulfill_ = function(value) {
        872 goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);
        873 this.state_ = goog.Promise.State_.PENDING;
        874 this.resolve_(goog.Promise.State_.FULFILLED, value);
        875};
        876
        877
        878/**
        879 * Unblocks the Promise and rejects it with the given rejection reason.
        880 *
        881 * @param {*} reason
        882 * @private
        883 */
        884goog.Promise.prototype.unblockAndReject_ = function(reason) {
        885 goog.asserts.assert(this.state_ == goog.Promise.State_.BLOCKED);
        886 this.state_ = goog.Promise.State_.PENDING;
        887 this.resolve_(goog.Promise.State_.REJECTED, reason);
        888};
        889
        890
        891/**
        892 * Attempts to resolve a Promise with a given resolution state and value. This
        893 * is a no-op if the given Promise has already been resolved.
        894 *
        895 * If the given result is a Thenable (such as another Promise), the Promise will
        896 * be settled with the same state and result as the Thenable once it is itself
        897 * settled.
        898 *
        899 * If the given result is not a Thenable, the Promise will be settled (fulfilled
        900 * or rejected) with that result based on the given state.
        901 *
        902 * @see http://promisesaplus.com/#the_promise_resolution_procedure
        903 *
        904 * @param {goog.Promise.State_} state
        905 * @param {*} x The result to apply to the Promise.
        906 * @private
        907 */
        908goog.Promise.prototype.resolve_ = function(state, x) {
        909 if (this.state_ != goog.Promise.State_.PENDING) {
        910 return;
        911 }
        912
        913 if (this == x) {
        914 state = goog.Promise.State_.REJECTED;
        915 x = new TypeError('Promise cannot resolve to itself');
        916 }
        917
        918 this.state_ = goog.Promise.State_.BLOCKED;
        919 var isThenable = goog.Promise.maybeThen_(
        920 x, this.unblockAndFulfill_, this.unblockAndReject_, this);
        921 if (isThenable) {
        922 return;
        923 }
        924
        925 this.result_ = x;
        926 this.state_ = state;
        927 // Since we can no longer be canceled, remove link to parent, so that the
        928 // child promise does not keep the parent promise alive.
        929 this.parent_ = null;
        930 this.scheduleCallbacks_();
        931
        932 if (state == goog.Promise.State_.REJECTED &&
        933 !(x instanceof goog.Promise.CancellationError)) {
        934 goog.Promise.addUnhandledRejection_(this, x);
        935 }
        936};
        937
        938
        939/**
        940 * Invokes the "then" method of an input value if that value is a Thenable. This
        941 * is a no-op if the value is not thenable.
        942 *
        943 * @param {?} value A potentially thenable value.
        944 * @param {!Function} onFulfilled
        945 * @param {!Function} onRejected
        946 * @param {?} context
        947 * @return {boolean} Whether the input value was thenable.
        948 * @private
        949 */
        950goog.Promise.maybeThen_ = function(value, onFulfilled, onRejected, context) {
        951 if (value instanceof goog.Promise) {
        952 value.thenVoid(onFulfilled, onRejected, context);
        953 return true;
        954 } else if (goog.Thenable.isImplementedBy(value)) {
        955 value = /** @type {!goog.Thenable} */ (value);
        956 value.then(onFulfilled, onRejected, context);
        957 return true;
        958 } else if (goog.isObject(value)) {
        959 try {
        960 var then = value['then'];
        961 if (goog.isFunction(then)) {
        962 goog.Promise.tryThen_(
        963 value, then, onFulfilled, onRejected, context);
        964 return true;
        965 }
        966 } catch (e) {
        967 onRejected.call(context, e);
        968 return true;
        969 }
        970 }
        971
        972 return false;
        973};
        974
        975
        976/**
        977 * Attempts to call the {@code then} method on an object in the hopes that it is
        978 * a Promise-compatible instance. This allows interoperation between different
        979 * Promise implementations, however a non-compliant object may cause a Promise
        980 * to hang indefinitely. If the {@code then} method throws an exception, the
        981 * dependent Promise will be rejected with the thrown value.
        982 *
        983 * @see http://promisesaplus.com/#point-70
        984 *
        985 * @param {Thenable} thenable An object with a {@code then} method that may be
        986 * compatible with the Promise/A+ specification.
        987 * @param {!Function} then The {@code then} method of the Thenable object.
        988 * @param {!Function} onFulfilled
        989 * @param {!Function} onRejected
        990 * @param {*} context
        991 * @private
        992 */
        993goog.Promise.tryThen_ = function(
        994 thenable, then, onFulfilled, onRejected, context) {
        995
        996 var called = false;
        997 var resolve = function(value) {
        998 if (!called) {
        999 called = true;
        1000 onFulfilled.call(context, value);
        1001 }
        1002 };
        1003
        1004 var reject = function(reason) {
        1005 if (!called) {
        1006 called = true;
        1007 onRejected.call(context, reason);
        1008 }
        1009 };
        1010
        1011 try {
        1012 then.call(thenable, resolve, reject);
        1013 } catch (e) {
        1014 reject(e);
        1015 }
        1016};
        1017
        1018
        1019/**
        1020 * Executes the pending callbacks of a settled Promise after a timeout.
        1021 *
        1022 * Section 2.2.4 of the Promises/A+ specification requires that Promise
        1023 * callbacks must only be invoked from a call stack that only contains Promise
        1024 * implementation code, which we accomplish by invoking callback execution after
        1025 * a timeout. If {@code startExecution_} is called multiple times for the same
        1026 * Promise, the callback chain will be evaluated only once. Additional callbacks
        1027 * may be added during the evaluation phase, and will be executed in the same
        1028 * event loop.
        1029 *
        1030 * All Promises added to the waiting list during the same browser event loop
        1031 * will be executed in one batch to avoid using a separate timeout per Promise.
        1032 *
        1033 * @private
        1034 */
        1035goog.Promise.prototype.scheduleCallbacks_ = function() {
        1036 if (!this.executing_) {
        1037 this.executing_ = true;
        1038 goog.async.run(this.executeCallbacks_, this);
        1039 }
        1040};
        1041
        1042
        1043/**
        1044 * @return {boolean} Whether there are any pending callbacks queued.
        1045 * @private
        1046 */
        1047goog.Promise.prototype.hasEntry_ = function() {
        1048 return !!this.callbackEntries_;
        1049};
        1050
        1051
        1052/**
        1053 * @param {goog.Promise.CallbackEntry_} entry
        1054 * @private
        1055 */
        1056goog.Promise.prototype.queueEntry_ = function(entry) {
        1057 goog.asserts.assert(entry.onFulfilled != null);
        1058
        1059 if (this.callbackEntriesTail_) {
        1060 this.callbackEntriesTail_.next = entry;
        1061 this.callbackEntriesTail_ = entry;
        1062 } else {
        1063 // It the work queue was empty set the head too.
        1064 this.callbackEntries_ = entry;
        1065 this.callbackEntriesTail_ = entry;
        1066 }
        1067};
        1068
        1069
        1070/**
        1071 * @return {goog.Promise.CallbackEntry_} entry
        1072 * @private
        1073 */
        1074goog.Promise.prototype.popEntry_ = function() {
        1075 var entry = null;
        1076 if (this.callbackEntries_) {
        1077 entry = this.callbackEntries_;
        1078 this.callbackEntries_ = entry.next;
        1079 entry.next = null;
        1080 }
        1081 // It the work queue is empty clear the tail too.
        1082 if (!this.callbackEntries_) {
        1083 this.callbackEntriesTail_ = null;
        1084 }
        1085
        1086 if (entry != null) {
        1087 goog.asserts.assert(entry.onFulfilled != null);
        1088 }
        1089 return entry;
        1090};
        1091
        1092
        1093/**
        1094 * @param {goog.Promise.CallbackEntry_} previous
        1095 * @private
        1096 */
        1097goog.Promise.prototype.removeEntryAfter_ = function(previous) {
        1098 goog.asserts.assert(this.callbackEntries_);
        1099 goog.asserts.assert(previous != null);
        1100 // If the last entry is being removed, update the tail
        1101 if (previous.next == this.callbackEntriesTail_) {
        1102 this.callbackEntriesTail_ = previous;
        1103 }
        1104
        1105 previous.next = previous.next.next;
        1106};
        1107
        1108
        1109/**
        1110 * Executes all pending callbacks for this Promise.
        1111 *
        1112 * @private
        1113 */
        1114goog.Promise.prototype.executeCallbacks_ = function() {
        1115 var entry = null;
        1116 while (entry = this.popEntry_()) {
        1117 if (goog.Promise.LONG_STACK_TRACES) {
        1118 this.currentStep_++;
        1119 }
        1120 this.executeCallback_(entry, this.state_, this.result_);
        1121 }
        1122 this.executing_ = false;
        1123};
        1124
        1125
        1126/**
        1127 * Executes a pending callback for this Promise. Invokes an {@code onFulfilled}
        1128 * or {@code onRejected} callback based on the settled state of the Promise.
        1129 *
        1130 * @param {!goog.Promise.CallbackEntry_} callbackEntry An entry containing the
        1131 * onFulfilled and/or onRejected callbacks for this step.
        1132 * @param {goog.Promise.State_} state The resolution status of the Promise,
        1133 * either FULFILLED or REJECTED.
        1134 * @param {*} result The settled result of the Promise.
        1135 * @private
        1136 */
        1137goog.Promise.prototype.executeCallback_ = function(
        1138 callbackEntry, state, result) {
        1139 // Cancel an unhandled rejection if the then/thenVoid call had an onRejected.
        1140 if (state == goog.Promise.State_.REJECTED &&
        1141 callbackEntry.onRejected && !callbackEntry.always) {
        1142 this.removeUnhandledRejection_();
        1143 }
        1144
        1145 if (callbackEntry.child) {
        1146 // When the parent is settled, the child no longer needs to hold on to it,
        1147 // as the parent can no longer be canceled.
        1148 callbackEntry.child.parent_ = null;
        1149 goog.Promise.invokeCallback_(callbackEntry, state, result);
        1150 } else {
        1151 // Callbacks created with thenAlways or thenVoid do not have the rejection
        1152 // handling code normally set up in the child Promise.
        1153 try {
        1154 callbackEntry.always ?
        1155 callbackEntry.onFulfilled.call(callbackEntry.context) :
        1156 goog.Promise.invokeCallback_(callbackEntry, state, result);
        1157 } catch (err) {
        1158 goog.Promise.handleRejection_.call(null, err);
        1159 }
        1160 }
        1161 goog.Promise.returnEntry_(callbackEntry);
        1162};
        1163
        1164
        1165/**
        1166 * Executes the onFulfilled or onRejected callback for a callbackEntry.
        1167 *
        1168 * @param {!goog.Promise.CallbackEntry_} callbackEntry
        1169 * @param {goog.Promise.State_} state
        1170 * @param {*} result
        1171 * @private
        1172 */
        1173goog.Promise.invokeCallback_ = function(callbackEntry, state, result) {
        1174 if (state == goog.Promise.State_.FULFILLED) {
        1175 callbackEntry.onFulfilled.call(callbackEntry.context, result);
        1176 } else if (callbackEntry.onRejected) {
        1177 callbackEntry.onRejected.call(callbackEntry.context, result);
        1178 }
        1179};
        1180
        1181
        1182/**
        1183 * Records a stack trace entry for functions that call {@code then} or the
        1184 * Promise constructor. May be disabled by unsetting {@code LONG_STACK_TRACES}.
        1185 *
        1186 * @param {!Error} err An Error object created by the calling function for
        1187 * providing a stack trace.
        1188 * @private
        1189 */
        1190goog.Promise.prototype.addStackTrace_ = function(err) {
        1191 if (goog.Promise.LONG_STACK_TRACES && goog.isString(err.stack)) {
        1192 // Extract the third line of the stack trace, which is the entry for the
        1193 // user function that called into Promise code.
        1194 var trace = err.stack.split('\n', 4)[3];
        1195 var message = err.message;
        1196
        1197 // Pad the message to align the traces.
        1198 message += Array(11 - message.length).join(' ');
        1199 this.stack_.push(message + trace);
        1200 }
        1201};
        1202
        1203
        1204/**
        1205 * Adds extra stack trace information to an exception for the list of
        1206 * asynchronous {@code then} calls that have been run for this Promise. Stack
        1207 * trace information is recorded in {@see #addStackTrace_}, and appended to
        1208 * rethrown errors when {@code LONG_STACK_TRACES} is enabled.
        1209 *
        1210 * @param {*} err An unhandled exception captured during callback execution.
        1211 * @private
        1212 */
        1213goog.Promise.prototype.appendLongStack_ = function(err) {
        1214 if (goog.Promise.LONG_STACK_TRACES &&
        1215 err && goog.isString(err.stack) && this.stack_.length) {
        1216 var longTrace = ['Promise trace:'];
        1217
        1218 for (var promise = this; promise; promise = promise.parent_) {
        1219 for (var i = this.currentStep_; i >= 0; i--) {
        1220 longTrace.push(promise.stack_[i]);
        1221 }
        1222 longTrace.push('Value: ' +
        1223 '[' + (promise.state_ == goog.Promise.State_.REJECTED ?
        1224 'REJECTED' : 'FULFILLED') + '] ' +
        1225 '<' + String(promise.result_) + '>');
        1226 }
        1227 err.stack += '\n\n' + longTrace.join('\n');
        1228 }
        1229};
        1230
        1231
        1232/**
        1233 * Marks this rejected Promise as having being handled. Also marks any parent
        1234 * Promises in the rejected state as handled. The rejection handler will no
        1235 * longer be invoked for this Promise (if it has not been called already).
        1236 *
        1237 * @private
        1238 */
        1239goog.Promise.prototype.removeUnhandledRejection_ = function() {
        1240 if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
        1241 for (var p = this; p && p.unhandledRejectionId_; p = p.parent_) {
        1242 goog.global.clearTimeout(p.unhandledRejectionId_);
        1243 p.unhandledRejectionId_ = 0;
        1244 }
        1245 } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
        1246 for (var p = this; p && p.hadUnhandledRejection_; p = p.parent_) {
        1247 p.hadUnhandledRejection_ = false;
        1248 }
        1249 }
        1250};
        1251
        1252
        1253/**
        1254 * Marks this rejected Promise as unhandled. If no {@code onRejected} callback
        1255 * is called for this Promise before the {@code UNHANDLED_REJECTION_DELAY}
        1256 * expires, the reason will be passed to the unhandled rejection handler. The
        1257 * handler typically rethrows the rejection reason so that it becomes visible in
        1258 * the developer console.
        1259 *
        1260 * @param {!goog.Promise} promise The rejected Promise.
        1261 * @param {*} reason The Promise rejection reason.
        1262 * @private
        1263 */
        1264goog.Promise.addUnhandledRejection_ = function(promise, reason) {
        1265 if (goog.Promise.UNHANDLED_REJECTION_DELAY > 0) {
        1266 promise.unhandledRejectionId_ = goog.global.setTimeout(function() {
        1267 promise.appendLongStack_(reason);
        1268 goog.Promise.handleRejection_.call(null, reason);
        1269 }, goog.Promise.UNHANDLED_REJECTION_DELAY);
        1270
        1271 } else if (goog.Promise.UNHANDLED_REJECTION_DELAY == 0) {
        1272 promise.hadUnhandledRejection_ = true;
        1273 goog.async.run(function() {
        1274 if (promise.hadUnhandledRejection_) {
        1275 promise.appendLongStack_(reason);
        1276 goog.Promise.handleRejection_.call(null, reason);
        1277 }
        1278 });
        1279 }
        1280};
        1281
        1282
        1283/**
        1284 * A method that is invoked with the rejection reasons for Promises that are
        1285 * rejected but have no {@code onRejected} callbacks registered yet.
        1286 * @type {function(*)}
        1287 * @private
        1288 */
        1289goog.Promise.handleRejection_ = goog.async.throwException;
        1290
        1291
        1292/**
        1293 * Sets a handler that will be called with reasons from unhandled rejected
        1294 * Promises. If the rejected Promise (or one of its descendants) has an
        1295 * {@code onRejected} callback registered, the rejection will be considered
        1296 * handled, and the rejection handler will not be called.
        1297 *
        1298 * By default, unhandled rejections are rethrown so that the error may be
        1299 * captured by the developer console or a {@code window.onerror} handler.
        1300 *
        1301 * @param {function(*)} handler A function that will be called with reasons from
        1302 * rejected Promises. Defaults to {@code goog.async.throwException}.
        1303 */
        1304goog.Promise.setUnhandledRejectionHandler = function(handler) {
        1305 goog.Promise.handleRejection_ = handler;
        1306};
        1307
        1308
        1309
        1310/**
        1311 * Error used as a rejection reason for canceled Promises.
        1312 *
        1313 * @param {string=} opt_message
        1314 * @constructor
        1315 * @extends {goog.debug.Error}
        1316 * @final
        1317 */
        1318goog.Promise.CancellationError = function(opt_message) {
        1319 goog.Promise.CancellationError.base(this, 'constructor', opt_message);
        1320};
        1321goog.inherits(goog.Promise.CancellationError, goog.debug.Error);
        1322
        1323
        1324/** @override */
        1325goog.Promise.CancellationError.prototype.name = 'cancel';
        1326
        1327
        1328
        1329/**
        1330 * Internal implementation of the resolver interface.
        1331 *
        1332 * @param {!goog.Promise<TYPE>} promise
        1333 * @param {function((TYPE|goog.Promise<TYPE>|Thenable)=)} resolve
        1334 * @param {function(*=): void} reject
        1335 * @implements {goog.promise.Resolver<TYPE>}
        1336 * @final @struct
        1337 * @constructor
        1338 * @private
        1339 * @template TYPE
        1340 */
        1341goog.Promise.Resolver_ = function(promise, resolve, reject) {
        1342 /** @const */
        1343 this.promise = promise;
        1344
        1345 /** @const */
        1346 this.resolve = resolve;
        1347
        1348 /** @const */
        1349 this.reject = reject;
        1350};
        \ No newline at end of file diff --git a/docs/source/lib/goog/promise/resolver.js.src.html b/docs/source/lib/goog/promise/resolver.js.src.html new file mode 100644 index 0000000..2c307f5 --- /dev/null +++ b/docs/source/lib/goog/promise/resolver.js.src.html @@ -0,0 +1 @@ +resolver.js

        lib/goog/promise/resolver.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
        15goog.provide('goog.promise.Resolver');
        16
        17
        18
        19/**
        20 * Resolver interface for promises. The resolver is a convenience interface that
        21 * bundles the promise and its associated resolve and reject functions together,
        22 * for cases where the resolver needs to be persisted internally.
        23 *
        24 * @interface
        25 * @template TYPE
        26 */
        27goog.promise.Resolver = function() {};
        28
        29
        30/**
        31 * The promise that created this resolver.
        32 * @type {!goog.Promise<TYPE>}
        33 */
        34goog.promise.Resolver.prototype.promise;
        35
        36
        37/**
        38 * Resolves this resolver with the specified value.
        39 * @type {function((TYPE|goog.Promise<TYPE>|Thenable)=)}
        40 */
        41goog.promise.Resolver.prototype.resolve;
        42
        43
        44/**
        45 * Rejects this resolver with the specified reason.
        46 * @type {function(*=): void}
        47 */
        48goog.promise.Resolver.prototype.reject;
        \ No newline at end of file diff --git a/docs/source/lib/goog/promise/thenable.js.src.html b/docs/source/lib/goog/promise/thenable.js.src.html new file mode 100644 index 0000000..8fe1d98 --- /dev/null +++ b/docs/source/lib/goog/promise/thenable.js.src.html @@ -0,0 +1 @@ +thenable.js

        lib/goog/promise/thenable.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
        15goog.provide('goog.Thenable');
        16
        17
        18
        19/**
        20 * Provides a more strict interface for Thenables in terms of
        21 * http://promisesaplus.com for interop with {@see goog.Promise}.
        22 *
        23 * @interface
        24 * @extends {IThenable<TYPE>}
        25 * @template TYPE
        26 */
        27goog.Thenable = function() {};
        28
        29
        30/**
        31 * Adds callbacks that will operate on the result of the Thenable, returning a
        32 * new child Promise.
        33 *
        34 * If the Thenable is fulfilled, the {@code onFulfilled} callback will be
        35 * invoked with the fulfillment value as argument, and the child Promise will
        36 * be fulfilled with the return value of the callback. If the callback throws
        37 * an exception, the child Promise will be rejected with the thrown value
        38 * instead.
        39 *
        40 * If the Thenable is rejected, the {@code onRejected} callback will be invoked
        41 * with the rejection reason as argument, and the child Promise will be rejected
        42 * with the return value of the callback or thrown value.
        43 *
        44 * @param {?(function(this:THIS, TYPE): VALUE)=} opt_onFulfilled A
        45 * function that will be invoked with the fulfillment value if the Promise
        46 * is fullfilled.
        47 * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will
        48 * be invoked with the rejection reason if the Promise is rejected.
        49 * @param {THIS=} opt_context An optional context object that will be the
        50 * execution context for the callbacks. By default, functions are executed
        51 * with the default this.
        52 *
        53 * @return {RESULT} A new Promise that will receive the result
        54 * of the fulfillment or rejection callback.
        55 * @template VALUE
        56 * @template THIS
        57 *
        58 * When a Promise (or thenable) is returned from the fulfilled callback,
        59 * the result is the payload of that promise, not the promise itself.
        60 *
        61 * @template RESULT := type('goog.Promise',
        62 * cond(isUnknown(VALUE), unknown(),
        63 * mapunion(VALUE, (V) =>
        64 * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'),
        65 * templateTypeOf(V, 0),
        66 * cond(sub(V, 'Thenable'),
        67 * unknown(),
        68 * V)))))
        69 * =:
        70 *
        71 */
        72goog.Thenable.prototype.then = function(opt_onFulfilled, opt_onRejected,
        73 opt_context) {};
        74
        75
        76/**
        77 * An expando property to indicate that an object implements
        78 * {@code goog.Thenable}.
        79 *
        80 * {@see addImplementation}.
        81 *
        82 * @const
        83 */
        84goog.Thenable.IMPLEMENTED_BY_PROP = '$goog_Thenable';
        85
        86
        87/**
        88 * Marks a given class (constructor) as an implementation of Thenable, so
        89 * that we can query that fact at runtime. The class must have already
        90 * implemented the interface.
        91 * Exports a 'then' method on the constructor prototype, so that the objects
        92 * also implement the extern {@see goog.Thenable} interface for interop with
        93 * other Promise implementations.
        94 * @param {function(new:goog.Thenable,...?)} ctor The class constructor. The
        95 * corresponding class must have already implemented the interface.
        96 */
        97goog.Thenable.addImplementation = function(ctor) {
        98 goog.exportProperty(ctor.prototype, 'then', ctor.prototype.then);
        99 if (COMPILED) {
        100 ctor.prototype[goog.Thenable.IMPLEMENTED_BY_PROP] = true;
        101 } else {
        102 // Avoids dictionary access in uncompiled mode.
        103 ctor.prototype.$goog_Thenable = true;
        104 }
        105};
        106
        107
        108/**
        109 * @param {*} object
        110 * @return {boolean} Whether a given instance implements {@code goog.Thenable}.
        111 * The class/superclass of the instance must call {@code addImplementation}.
        112 */
        113goog.Thenable.isImplementedBy = function(object) {
        114 if (!object) {
        115 return false;
        116 }
        117 try {
        118 if (COMPILED) {
        119 return !!object[goog.Thenable.IMPLEMENTED_BY_PROP];
        120 }
        121 return !!object.$goog_Thenable;
        122 } catch (e) {
        123 // Property access seems to be forbidden.
        124 return false;
        125 }
        126};
        \ No newline at end of file diff --git a/docs/source/lib/goog/reflect/reflect.js.src.html b/docs/source/lib/goog/reflect/reflect.js.src.html new file mode 100644 index 0000000..d849740 --- /dev/null +++ b/docs/source/lib/goog/reflect/reflect.js.src.html @@ -0,0 +1 @@ +reflect.js

        lib/goog/reflect/reflect.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 Useful compiler idioms.
        17 *
        18 * @author johnlenz@google.com (John Lenz)
        19 */
        20
        21goog.provide('goog.reflect');
        22
        23
        24/**
        25 * Syntax for object literal casts.
        26 * @see http://go/jscompiler-renaming
        27 * @see https://github.com/google/closure-compiler/wiki/Type-Based-Property-Renaming
        28 *
        29 * Use this if you have an object literal whose keys need to have the same names
        30 * as the properties of some class even after they are renamed by the compiler.
        31 *
        32 * @param {!Function} type Type to cast to.
        33 * @param {Object} object Object literal to cast.
        34 * @return {Object} The object literal.
        35 */
        36goog.reflect.object = function(type, object) {
        37 return object;
        38};
        39
        40
        41/**
        42 * To assert to the compiler that an operation is needed when it would
        43 * otherwise be stripped. For example:
        44 * <code>
        45 * // Force a layout
        46 * goog.reflect.sinkValue(dialog.offsetHeight);
        47 * </code>
        48 * @type {!Function}
        49 */
        50goog.reflect.sinkValue = function(x) {
        51 goog.reflect.sinkValue[' '](x);
        52 return x;
        53};
        54
        55
        56/**
        57 * The compiler should optimize this function away iff no one ever uses
        58 * goog.reflect.sinkValue.
        59 */
        60goog.reflect.sinkValue[' '] = goog.nullFunction;
        61
        62
        63/**
        64 * Check if a property can be accessed without throwing an exception.
        65 * @param {Object} obj The owner of the property.
        66 * @param {string} prop The property name.
        67 * @return {boolean} Whether the property is accessible. Will also return true
        68 * if obj is null.
        69 */
        70goog.reflect.canAccessProperty = function(obj, prop) {
        71 /** @preserveTry */
        72 try {
        73 goog.reflect.sinkValue(obj[prop]);
        74 return true;
        75 } catch (e) {}
        76 return false;
        77};
        \ No newline at end of file diff --git a/docs/source/lib/goog/string/const.js.src.html b/docs/source/lib/goog/string/const.js.src.html new file mode 100644 index 0000000..dbe5526 --- /dev/null +++ b/docs/source/lib/goog/string/const.js.src.html @@ -0,0 +1 @@ +const.js

        lib/goog/string/const.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
        15goog.provide('goog.string.Const');
        16
        17goog.require('goog.asserts');
        18goog.require('goog.string.TypedString');
        19
        20
        21
        22/**
        23 * Wrapper for compile-time-constant strings.
        24 *
        25 * Const is a wrapper for strings that can only be created from program
        26 * constants (i.e., string literals). This property relies on a custom Closure
        27 * compiler check that {@code goog.string.Const.from} is only invoked on
        28 * compile-time-constant expressions.
        29 *
        30 * Const is useful in APIs whose correct and secure use requires that certain
        31 * arguments are not attacker controlled: Compile-time constants are inherently
        32 * under the control of the application and not under control of external
        33 * attackers, and hence are safe to use in such contexts.
        34 *
        35 * Instances of this type must be created via its factory method
        36 * {@code goog.string.Const.from} and not by invoking its constructor. The
        37 * constructor intentionally takes no parameters and the type is immutable;
        38 * hence only a default instance corresponding to the empty string can be
        39 * obtained via constructor invocation.
        40 *
        41 * @see goog.string.Const#from
        42 * @constructor
        43 * @final
        44 * @struct
        45 * @implements {goog.string.TypedString}
        46 */
        47goog.string.Const = function() {
        48 /**
        49 * The wrapped value of this Const object. The field has a purposely ugly
        50 * name to make (non-compiled) code that attempts to directly access this
        51 * field stand out.
        52 * @private {string}
        53 */
        54 this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ = '';
        55
        56 /**
        57 * A type marker used to implement additional run-time type checking.
        58 * @see goog.string.Const#unwrap
        59 * @const
        60 * @private
        61 */
        62 this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ =
        63 goog.string.Const.TYPE_MARKER_;
        64};
        65
        66
        67/**
        68 * @override
        69 * @const
        70 */
        71goog.string.Const.prototype.implementsGoogStringTypedString = true;
        72
        73
        74/**
        75 * Returns this Const's value a string.
        76 *
        77 * IMPORTANT: In code where it is security-relevant that an object's type is
        78 * indeed {@code goog.string.Const}, use {@code goog.string.Const.unwrap}
        79 * instead of this method.
        80 *
        81 * @see goog.string.Const#unwrap
        82 * @override
        83 */
        84goog.string.Const.prototype.getTypedStringValue = function() {
        85 return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_;
        86};
        87
        88
        89/**
        90 * Returns a debug-string representation of this value.
        91 *
        92 * To obtain the actual string value wrapped inside an object of this type,
        93 * use {@code goog.string.Const.unwrap}.
        94 *
        95 * @see goog.string.Const#unwrap
        96 * @override
        97 */
        98goog.string.Const.prototype.toString = function() {
        99 return 'Const{' +
        100 this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ +
        101 '}';
        102};
        103
        104
        105/**
        106 * Performs a runtime check that the provided object is indeed an instance
        107 * of {@code goog.string.Const}, and returns its value.
        108 * @param {!goog.string.Const} stringConst The object to extract from.
        109 * @return {string} The Const object's contained string, unless the run-time
        110 * type check fails. In that case, {@code unwrap} returns an innocuous
        111 * string, or, if assertions are enabled, throws
        112 * {@code goog.asserts.AssertionError}.
        113 */
        114goog.string.Const.unwrap = function(stringConst) {
        115 // Perform additional run-time type-checking to ensure that stringConst is
        116 // indeed an instance of the expected type. This provides some additional
        117 // protection against security bugs due to application code that disables type
        118 // checks.
        119 if (stringConst instanceof goog.string.Const &&
        120 stringConst.constructor === goog.string.Const &&
        121 stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ ===
        122 goog.string.Const.TYPE_MARKER_) {
        123 return stringConst.
        124 stringConstValueWithSecurityContract__googStringSecurityPrivate_;
        125 } else {
        126 goog.asserts.fail('expected object of type Const, got \'' +
        127 stringConst + '\'');
        128 return 'type_error:Const';
        129 }
        130};
        131
        132
        133/**
        134 * Creates a Const object from a compile-time constant string.
        135 *
        136 * It is illegal to invoke this function on an expression whose
        137 * compile-time-contant value cannot be determined by the Closure compiler.
        138 *
        139 * Correct invocations include,
        140 * <pre>
        141 * var s = goog.string.Const.from('hello');
        142 * var t = goog.string.Const.from('hello' + 'world');
        143 * </pre>
        144 *
        145 * In contrast, the following are illegal:
        146 * <pre>
        147 * var s = goog.string.Const.from(getHello());
        148 * var t = goog.string.Const.from('hello' + world);
        149 * </pre>
        150 *
        151 * TODO(xtof): Compile-time checks that this function is only called
        152 * with compile-time constant expressions.
        153 *
        154 * @param {string} s A constant string from which to create a Const.
        155 * @return {!goog.string.Const} A Const object initialized to stringConst.
        156 */
        157goog.string.Const.from = function(s) {
        158 return goog.string.Const.create__googStringSecurityPrivate_(s);
        159};
        160
        161
        162/**
        163 * Type marker for the Const type, used to implement additional run-time
        164 * type checking.
        165 * @const {!Object}
        166 * @private
        167 */
        168goog.string.Const.TYPE_MARKER_ = {};
        169
        170
        171/**
        172 * Utility method to create Const instances.
        173 * @param {string} s The string to initialize the Const object with.
        174 * @return {!goog.string.Const} The initialized Const object.
        175 * @private
        176 */
        177goog.string.Const.create__googStringSecurityPrivate_ = function(s) {
        178 var stringConst = new goog.string.Const();
        179 stringConst.stringConstValueWithSecurityContract__googStringSecurityPrivate_ =
        180 s;
        181 return stringConst;
        182};
        \ No newline at end of file diff --git a/docs/source/lib/goog/string/string.js.src.html b/docs/source/lib/goog/string/string.js.src.html index 76c8210..a7cdf8f 100644 --- a/docs/source/lib/goog/string/string.js.src.html +++ b/docs/source/lib/goog/string/string.js.src.html @@ -1 +1 @@ -string.js

        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 */
        23goog.provide('goog.string');
        24goog.provide('goog.string.Unicode');
        25
        26
        27/**
        28 * Common Unicode string characters.
        29 * @enum {string}
        30 */
        31goog.string.Unicode = {
        32 NBSP: '\xa0'
        33};
        34
        35
        36/**
        37 * Fast prefix-checker.
        38 * @param {string} str The string to check.
        39 * @param {string} prefix A string to look for at the start of {@code str}.
        40 * @return {boolean} True if {@code str} begins with {@code prefix}.
        41 */
        42goog.string.startsWith = function(str, prefix) {
        43 return str.lastIndexOf(prefix, 0) == 0;
        44};
        45
        46
        47/**
        48 * Fast suffix-checker.
        49 * @param {string} str The string to check.
        50 * @param {string} suffix A string to look for at the end of {@code str}.
        51 * @return {boolean} True if {@code str} ends with {@code suffix}.
        52 */
        53goog.string.endsWith = function(str, suffix) {
        54 var l = str.length - suffix.length;
        55 return l >= 0 && str.indexOf(suffix, l) == l;
        56};
        57
        58
        59/**
        60 * Case-insensitive prefix-checker.
        61 * @param {string} str The string to check.
        62 * @param {string} prefix A string to look for at the end of {@code str}.
        63 * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring
        64 * case).
        65 */
        66goog.string.caseInsensitiveStartsWith = function(str, prefix) {
        67 return goog.string.caseInsensitiveCompare(
        68 prefix, str.substr(0, prefix.length)) == 0;
        69};
        70
        71
        72/**
        73 * Case-insensitive suffix-checker.
        74 * @param {string} str The string to check.
        75 * @param {string} suffix A string to look for at the end of {@code str}.
        76 * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring
        77 * case).
        78 */
        79goog.string.caseInsensitiveEndsWith = function(str, suffix) {
        80 return goog.string.caseInsensitiveCompare(
        81 suffix, str.substr(str.length - suffix.length, suffix.length)) == 0;
        82};
        83
        84
        85/**
        86 * Case-insensitive equality checker.
        87 * @param {string} str1 First string to check.
        88 * @param {string} str2 Second string to check.
        89 * @return {boolean} True if {@code str1} and {@code str2} are the same string,
        90 * ignoring case.
        91 */
        92goog.string.caseInsensitiveEquals = function(str1, str2) {
        93 return str1.toLowerCase() == str2.toLowerCase();
        94};
        95
        96
        97/**
        98 * Does simple python-style string substitution.
        99 * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
        100 * @param {string} str The string containing the pattern.
        101 * @param {...*} var_args The items to substitute into the pattern.
        102 * @return {string} A copy of {@code str} in which each occurrence of
        103 * {@code %s} has been replaced an argument from {@code var_args}.
        104 */
        105goog.string.subs = function(str, var_args) {
        106 var splitParts = str.split('%s');
        107 var returnString = '';
        108
        109 var subsArguments = Array.prototype.slice.call(arguments, 1);
        110 while (subsArguments.length &&
        111 // Replace up to the last split part. We are inserting in the
        112 // positions between split parts.
        113 splitParts.length > 1) {
        114 returnString += splitParts.shift() + subsArguments.shift();
        115 }
        116
        117 return returnString + splitParts.join('%s'); // Join unused '%s'
        118};
        119
        120
        121/**
        122 * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
        123 * and tabs) to a single space, and strips leading and trailing whitespace.
        124 * @param {string} str Input string.
        125 * @return {string} A copy of {@code str} with collapsed whitespace.
        126 */
        127goog.string.collapseWhitespace = function(str) {
        128 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        129 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        130 // include it in the regexp to enforce consistent cross-browser behavior.
        131 return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
        132};
        133
        134
        135/**
        136 * Checks if a string is empty or contains only whitespaces.
        137 * @param {string} str The string to check.
        138 * @return {boolean} True if {@code str} is empty or whitespace only.
        139 */
        140goog.string.isEmpty = function(str) {
        141 // testing length == 0 first is actually slower in all browsers (about the
        142 // same in Opera).
        143 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        144 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        145 // include it in the regexp to enforce consistent cross-browser behavior.
        146 return /^[\s\xa0]*$/.test(str);
        147};
        148
        149
        150/**
        151 * Checks if a string is null, undefined, empty or contains only whitespaces.
        152 * @param {*} str The string to check.
        153 * @return {boolean} True if{@code str} is null, undefined, empty, or
        154 * whitespace only.
        155 */
        156goog.string.isEmptySafe = function(str) {
        157 return goog.string.isEmpty(goog.string.makeSafe(str));
        158};
        159
        160
        161/**
        162 * Checks if a string is all breaking whitespace.
        163 * @param {string} str The string to check.
        164 * @return {boolean} Whether the string is all breaking whitespace.
        165 */
        166goog.string.isBreakingWhitespace = function(str) {
        167 return !/[^\t\n\r ]/.test(str);
        168};
        169
        170
        171/**
        172 * Checks if a string contains all letters.
        173 * @param {string} str string to check.
        174 * @return {boolean} True if {@code str} consists entirely of letters.
        175 */
        176goog.string.isAlpha = function(str) {
        177 return !/[^a-zA-Z]/.test(str);
        178};
        179
        180
        181/**
        182 * Checks if a string contains only numbers.
        183 * @param {*} str string to check. If not a string, it will be
        184 * casted to one.
        185 * @return {boolean} True if {@code str} is numeric.
        186 */
        187goog.string.isNumeric = function(str) {
        188 return !/[^0-9]/.test(str);
        189};
        190
        191
        192/**
        193 * Checks if a string contains only numbers or letters.
        194 * @param {string} str string to check.
        195 * @return {boolean} True if {@code str} is alphanumeric.
        196 */
        197goog.string.isAlphaNumeric = function(str) {
        198 return !/[^a-zA-Z0-9]/.test(str);
        199};
        200
        201
        202/**
        203 * Checks if a character is a space character.
        204 * @param {string} ch Character to check.
        205 * @return {boolean} True if {code ch} is a space.
        206 */
        207goog.string.isSpace = function(ch) {
        208 return ch == ' ';
        209};
        210
        211
        212/**
        213 * Checks if a character is a valid unicode character.
        214 * @param {string} ch Character to check.
        215 * @return {boolean} True if {code ch} is a valid unicode character.
        216 */
        217goog.string.isUnicodeChar = function(ch) {
        218 return ch.length == 1 && ch >= ' ' && ch <= '~' ||
        219 ch >= '\u0080' && ch <= '\uFFFD';
        220};
        221
        222
        223/**
        224 * Takes a string and replaces newlines with a space. Multiple lines are
        225 * replaced with a single space.
        226 * @param {string} str The string from which to strip newlines.
        227 * @return {string} A copy of {@code str} stripped of newlines.
        228 */
        229goog.string.stripNewlines = function(str) {
        230 return str.replace(/(\r\n|\r|\n)+/g, ' ');
        231};
        232
        233
        234/**
        235 * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
        236 * @param {string} str The string to in which to canonicalize newlines.
        237 * @return {string} {@code str} A copy of {@code} with canonicalized newlines.
        238 */
        239goog.string.canonicalizeNewlines = function(str) {
        240 return str.replace(/(\r\n|\r|\n)/g, '\n');
        241};
        242
        243
        244/**
        245 * Normalizes whitespace in a string, replacing all whitespace chars with
        246 * a space.
        247 * @param {string} str The string in which to normalize whitespace.
        248 * @return {string} A copy of {@code str} with all whitespace normalized.
        249 */
        250goog.string.normalizeWhitespace = function(str) {
        251 return str.replace(/\xa0|\s/g, ' ');
        252};
        253
        254
        255/**
        256 * Normalizes spaces in a string, replacing all consecutive spaces and tabs
        257 * with a single space. Replaces non-breaking space with a space.
        258 * @param {string} str The string in which to normalize spaces.
        259 * @return {string} A copy of {@code str} with all consecutive spaces and tabs
        260 * replaced with a single space.
        261 */
        262goog.string.normalizeSpaces = function(str) {
        263 return str.replace(/\xa0|[ \t]+/g, ' ');
        264};
        265
        266
        267/**
        268 * Removes the breaking spaces from the left and right of the string and
        269 * collapses the sequences of breaking spaces in the middle into single spaces.
        270 * The original and the result strings render the same way in HTML.
        271 * @param {string} str A string in which to collapse spaces.
        272 * @return {string} Copy of the string with normalized breaking spaces.
        273 */
        274goog.string.collapseBreakingSpaces = function(str) {
        275 return str.replace(/[\t\r\n ]+/g, ' ').replace(
        276 /^[\t\r\n ]+|[\t\r\n ]+$/g, '');
        277};
        278
        279
        280/**
        281 * Trims white spaces to the left and right of a string.
        282 * @param {string} str The string to trim.
        283 * @return {string} A trimmed copy of {@code str}.
        284 */
        285goog.string.trim = function(str) {
        286 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        287 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        288 // include it in the regexp to enforce consistent cross-browser behavior.
        289 return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
        290};
        291
        292
        293/**
        294 * Trims whitespaces at the left end of a string.
        295 * @param {string} str The string to left trim.
        296 * @return {string} A trimmed copy of {@code str}.
        297 */
        298goog.string.trimLeft = function(str) {
        299 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        300 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        301 // include it in the regexp to enforce consistent cross-browser behavior.
        302 return str.replace(/^[\s\xa0]+/, '');
        303};
        304
        305
        306/**
        307 * Trims whitespaces at the right end of a string.
        308 * @param {string} str The string to right trim.
        309 * @return {string} A trimmed copy of {@code str}.
        310 */
        311goog.string.trimRight = function(str) {
        312 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        313 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        314 // include it in the regexp to enforce consistent cross-browser behavior.
        315 return str.replace(/[\s\xa0]+$/, '');
        316};
        317
        318
        319/**
        320 * A string comparator that ignores case.
        321 * -1 = str1 less than str2
        322 * 0 = str1 equals str2
        323 * 1 = str1 greater than str2
        324 *
        325 * @param {string} str1 The string to compare.
        326 * @param {string} str2 The string to compare {@code str1} to.
        327 * @return {number} The comparator result, as described above.
        328 */
        329goog.string.caseInsensitiveCompare = function(str1, str2) {
        330 var test1 = String(str1).toLowerCase();
        331 var test2 = String(str2).toLowerCase();
        332
        333 if (test1 < test2) {
        334 return -1;
        335 } else if (test1 == test2) {
        336 return 0;
        337 } else {
        338 return 1;
        339 }
        340};
        341
        342
        343/**
        344 * Regular expression used for splitting a string into substrings of fractional
        345 * numbers, integers, and non-numeric characters.
        346 * @type {RegExp}
        347 * @private
        348 */
        349goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g;
        350
        351
        352/**
        353 * String comparison function that handles numbers in a way humans might expect.
        354 * Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The
        355 * comparison is mostly case-insensitive, though strings that are identical
        356 * except for case are sorted with the upper-case strings before lower-case.
        357 *
        358 * This comparison function is significantly slower (about 500x) than either
        359 * the default or the case-insensitive compare. It should not be used in
        360 * time-critical code, but should be fast enough to sort several hundred short
        361 * strings (like filenames) with a reasonable delay.
        362 *
        363 * @param {string} str1 The string to compare in a numerically sensitive way.
        364 * @param {string} str2 The string to compare {@code str1} to.
        365 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
        366 * 0 if str1 > str2.
        367 */
        368goog.string.numerateCompare = function(str1, str2) {
        369 if (str1 == str2) {
        370 return 0;
        371 }
        372 if (!str1) {
        373 return -1;
        374 }
        375 if (!str2) {
        376 return 1;
        377 }
        378
        379 // Using match to split the entire string ahead of time turns out to be faster
        380 // for most inputs than using RegExp.exec or iterating over each character.
        381 var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_);
        382 var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_);
        383
        384 var count = Math.min(tokens1.length, tokens2.length);
        385
        386 for (var i = 0; i < count; i++) {
        387 var a = tokens1[i];
        388 var b = tokens2[i];
        389
        390 // Compare pairs of tokens, returning if one token sorts before the other.
        391 if (a != b) {
        392
        393 // Only if both tokens are integers is a special comparison required.
        394 // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
        395 var num1 = parseInt(a, 10);
        396 if (!isNaN(num1)) {
        397 var num2 = parseInt(b, 10);
        398 if (!isNaN(num2) && num1 - num2) {
        399 return num1 - num2;
        400 }
        401 }
        402 return a < b ? -1 : 1;
        403 }
        404 }
        405
        406 // If one string is a substring of the other, the shorter string sorts first.
        407 if (tokens1.length != tokens2.length) {
        408 return tokens1.length - tokens2.length;
        409 }
        410
        411 // The two strings must be equivalent except for case (perfect equality is
        412 // tested at the head of the function.) Revert to default ASCII-betical string
        413 // comparison to stablize the sort.
        414 return str1 < str2 ? -1 : 1;
        415};
        416
        417
        418/**
        419 * URL-encodes a string
        420 * @param {*} str The string to url-encode.
        421 * @return {string} An encoded copy of {@code str} that is safe for urls.
        422 * Note that '#', ':', and other characters used to delimit portions
        423 * of URLs *will* be encoded.
        424 */
        425goog.string.urlEncode = function(str) {
        426 return encodeURIComponent(String(str));
        427};
        428
        429
        430/**
        431 * URL-decodes the string. We need to specially handle '+'s because
        432 * the javascript library doesn't convert them to spaces.
        433 * @param {string} str The string to url decode.
        434 * @return {string} The decoded {@code str}.
        435 */
        436goog.string.urlDecode = function(str) {
        437 return decodeURIComponent(str.replace(/\+/g, ' '));
        438};
        439
        440
        441/**
        442 * Converts \n to <br>s or <br />s.
        443 * @param {string} str The string in which to convert newlines.
        444 * @param {boolean=} opt_xml Whether to use XML compatible tags.
        445 * @return {string} A copy of {@code str} with converted newlines.
        446 */
        447goog.string.newLineToBr = function(str, opt_xml) {
        448 return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
        449};
        450
        451
        452/**
        453 * Escape double quote '"' characters in addition to '&', '<', and '>' so that a
        454 * string can be included in an HTML tag attribute value within double quotes.
        455 *
        456 * It should be noted that > doesn't need to be escaped for the HTML or XML to
        457 * be valid, but it has been decided to escape it for consistency with other
        458 * implementations.
        459 *
        460 * NOTE(user):
        461 * HtmlEscape is often called during the generation of large blocks of HTML.
        462 * Using statics for the regular expressions and strings is an optimization
        463 * that can more than half the amount of time IE spends in this function for
        464 * large apps, since strings and regexes both contribute to GC allocations.
        465 *
        466 * Testing for the presence of a character before escaping increases the number
        467 * of function calls, but actually provides a speed increase for the average
        468 * case -- since the average case often doesn't require the escaping of all 4
        469 * characters and indexOf() is much cheaper than replace().
        470 * The worst case does suffer slightly from the additional calls, therefore the
        471 * opt_isLikelyToContainHtmlChars option has been included for situations
        472 * where all 4 HTML entities are very likely to be present and need escaping.
        473 *
        474 * Some benchmarks (times tended to fluctuate +-0.05ms):
        475 * FireFox IE6
        476 * (no chars / average (mix of cases) / all 4 chars)
        477 * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
        478 * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
        479 * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
        480 *
        481 * An additional advantage of checking if replace actually needs to be called
        482 * is a reduction in the number of object allocations, so as the size of the
        483 * application grows the difference between the various methods would increase.
        484 *
        485 * @param {string} str string to be escaped.
        486 * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
        487 * if the character needs replacing - use this option if you expect each of
        488 * the characters to appear often. Leave false if you expect few html
        489 * characters to occur in your strings, such as if you are escaping HTML.
        490 * @return {string} An escaped copy of {@code str}.
        491 */
        492goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
        493
        494 if (opt_isLikelyToContainHtmlChars) {
        495 return str.replace(goog.string.amperRe_, '&amp;')
        496 .replace(goog.string.ltRe_, '&lt;')
        497 .replace(goog.string.gtRe_, '&gt;')
        498 .replace(goog.string.quotRe_, '&quot;');
        499
        500 } else {
        501 // quick test helps in the case when there are no chars to replace, in
        502 // worst case this makes barely a difference to the time taken
        503 if (!goog.string.allRe_.test(str)) return str;
        504
        505 // str.indexOf is faster than regex.test in this case
        506 if (str.indexOf('&') != -1) {
        507 str = str.replace(goog.string.amperRe_, '&amp;');
        508 }
        509 if (str.indexOf('<') != -1) {
        510 str = str.replace(goog.string.ltRe_, '&lt;');
        511 }
        512 if (str.indexOf('>') != -1) {
        513 str = str.replace(goog.string.gtRe_, '&gt;');
        514 }
        515 if (str.indexOf('"') != -1) {
        516 str = str.replace(goog.string.quotRe_, '&quot;');
        517 }
        518 return str;
        519 }
        520};
        521
        522
        523/**
        524 * Regular expression that matches an ampersand, for use in escaping.
        525 * @type {RegExp}
        526 * @private
        527 */
        528goog.string.amperRe_ = /&/g;
        529
        530
        531/**
        532 * Regular expression that matches a less than sign, for use in escaping.
        533 * @type {RegExp}
        534 * @private
        535 */
        536goog.string.ltRe_ = /</g;
        537
        538
        539/**
        540 * Regular expression that matches a greater than sign, for use in escaping.
        541 * @type {RegExp}
        542 * @private
        543 */
        544goog.string.gtRe_ = />/g;
        545
        546
        547/**
        548 * Regular expression that matches a double quote, for use in escaping.
        549 * @type {RegExp}
        550 * @private
        551 */
        552goog.string.quotRe_ = /\"/g;
        553
        554
        555/**
        556 * Regular expression that matches any character that needs to be escaped.
        557 * @type {RegExp}
        558 * @private
        559 */
        560goog.string.allRe_ = /[&<>\"]/;
        561
        562
        563/**
        564 * Unescapes an HTML string.
        565 *
        566 * @param {string} str The string to unescape.
        567 * @return {string} An unescaped copy of {@code str}.
        568 */
        569goog.string.unescapeEntities = function(str) {
        570 if (goog.string.contains(str, '&')) {
        571 // We are careful not to use a DOM if we do not have one. We use the []
        572 // notation so that the JSCompiler will not complain about these objects and
        573 // fields in the case where we have no DOM.
        574 if ('document' in goog.global) {
        575 return goog.string.unescapeEntitiesUsingDom_(str);
        576 } else {
        577 // Fall back on pure XML entities
        578 return goog.string.unescapePureXmlEntities_(str);
        579 }
        580 }
        581 return str;
        582};
        583
        584
        585/**
        586 * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
        587 * entities. This function is XSS-safe and whitespace-preserving.
        588 * @private
        589 * @param {string} str The string to unescape.
        590 * @return {string} The unescaped {@code str} string.
        591 */
        592goog.string.unescapeEntitiesUsingDom_ = function(str) {
        593 var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
        594 var div = document.createElement('div');
        595 // Match as many valid entity characters as possible. If the actual entity
        596 // happens to be shorter, it will still work as innerHTML will return the
        597 // trailing characters unchanged. Since the entity characters do not include
        598 // open angle bracket, there is no chance of XSS from the innerHTML use.
        599 // Since no whitespace is passed to innerHTML, whitespace is preserved.
        600 return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
        601 // Check for cached entity.
        602 var value = seen[s];
        603 if (value) {
        604 return value;
        605 }
        606 // Check for numeric entity.
        607 if (entity.charAt(0) == '#') {
        608 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
        609 var n = Number('0' + entity.substr(1));
        610 if (!isNaN(n)) {
        611 value = String.fromCharCode(n);
        612 }
        613 }
        614 // Fall back to innerHTML otherwise.
        615 if (!value) {
        616 // Append a non-entity character to avoid a bug in Webkit that parses
        617 // an invalid entity at the end of innerHTML text as the empty string.
        618 div.innerHTML = s + ' ';
        619 // Then remove the trailing character from the result.
        620 value = div.firstChild.nodeValue.slice(0, -1);
        621 }
        622 // Cache and return.
        623 return seen[s] = value;
        624 });
        625};
        626
        627
        628/**
        629 * Unescapes XML entities.
        630 * @private
        631 * @param {string} str The string to unescape.
        632 * @return {string} An unescaped copy of {@code str}.
        633 */
        634goog.string.unescapePureXmlEntities_ = function(str) {
        635 return str.replace(/&([^;]+);/g, function(s, entity) {
        636 switch (entity) {
        637 case 'amp':
        638 return '&';
        639 case 'lt':
        640 return '<';
        641 case 'gt':
        642 return '>';
        643 case 'quot':
        644 return '"';
        645 default:
        646 if (entity.charAt(0) == '#') {
        647 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
        648 var n = Number('0' + entity.substr(1));
        649 if (!isNaN(n)) {
        650 return String.fromCharCode(n);
        651 }
        652 }
        653 // For invalid entities we just return the entity
        654 return s;
        655 }
        656 });
        657};
        658
        659
        660/**
        661 * Regular expression that matches an HTML entity.
        662 * See also HTML5: Tokenization / Tokenizing character references.
        663 * @private
        664 * @type {!RegExp}
        665 */
        666goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
        667
        668
        669/**
        670 * Do escaping of whitespace to preserve spatial formatting. We use character
        671 * entity #160 to make it safer for xml.
        672 * @param {string} str The string in which to escape whitespace.
        673 * @param {boolean=} opt_xml Whether to use XML compatible tags.
        674 * @return {string} An escaped copy of {@code str}.
        675 */
        676goog.string.whitespaceEscape = function(str, opt_xml) {
        677 return goog.string.newLineToBr(str.replace(/ /g, ' &#160;'), opt_xml);
        678};
        679
        680
        681/**
        682 * Strip quote characters around a string. The second argument is a string of
        683 * characters to treat as quotes. This can be a single character or a string of
        684 * multiple character and in that case each of those are treated as possible
        685 * quote characters. For example:
        686 *
        687 * <pre>
        688 * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
        689 * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
        690 * </pre>
        691 *
        692 * @param {string} str The string to strip.
        693 * @param {string} quoteChars The quote characters to strip.
        694 * @return {string} A copy of {@code str} without the quotes.
        695 */
        696goog.string.stripQuotes = function(str, quoteChars) {
        697 var length = quoteChars.length;
        698 for (var i = 0; i < length; i++) {
        699 var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
        700 if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
        701 return str.substring(1, str.length - 1);
        702 }
        703 }
        704 return str;
        705};
        706
        707
        708/**
        709 * Truncates a string to a certain length and adds '...' if necessary. The
        710 * length also accounts for the ellipsis, so a maximum length of 10 and a string
        711 * 'Hello World!' produces 'Hello W...'.
        712 * @param {string} str The string to truncate.
        713 * @param {number} chars Max number of characters.
        714 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
        715 * characters from being cut off in the middle.
        716 * @return {string} The truncated {@code str} string.
        717 */
        718goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
        719 if (opt_protectEscapedCharacters) {
        720 str = goog.string.unescapeEntities(str);
        721 }
        722
        723 if (str.length > chars) {
        724 str = str.substring(0, chars - 3) + '...';
        725 }
        726
        727 if (opt_protectEscapedCharacters) {
        728 str = goog.string.htmlEscape(str);
        729 }
        730
        731 return str;
        732};
        733
        734
        735/**
        736 * Truncate a string in the middle, adding "..." if necessary,
        737 * and favoring the beginning of the string.
        738 * @param {string} str The string to truncate the middle of.
        739 * @param {number} chars Max number of characters.
        740 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
        741 * characters from being cutoff in the middle.
        742 * @param {number=} opt_trailingChars Optional number of trailing characters to
        743 * leave at the end of the string, instead of truncating as close to the
        744 * middle as possible.
        745 * @return {string} A truncated copy of {@code str}.
        746 */
        747goog.string.truncateMiddle = function(str, chars,
        748 opt_protectEscapedCharacters, opt_trailingChars) {
        749 if (opt_protectEscapedCharacters) {
        750 str = goog.string.unescapeEntities(str);
        751 }
        752
        753 if (opt_trailingChars && str.length > chars) {
        754 if (opt_trailingChars > chars) {
        755 opt_trailingChars = chars;
        756 }
        757 var endPoint = str.length - opt_trailingChars;
        758 var startPoint = chars - opt_trailingChars;
        759 str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
        760 } else if (str.length > chars) {
        761 // Favor the beginning of the string:
        762 var half = Math.floor(chars / 2);
        763 var endPos = str.length - half;
        764 half += chars % 2;
        765 str = str.substring(0, half) + '...' + str.substring(endPos);
        766 }
        767
        768 if (opt_protectEscapedCharacters) {
        769 str = goog.string.htmlEscape(str);
        770 }
        771
        772 return str;
        773};
        774
        775
        776/**
        777 * Special chars that need to be escaped for goog.string.quote.
        778 * @private
        779 * @type {Object}
        780 */
        781goog.string.specialEscapeChars_ = {
        782 '\0': '\\0',
        783 '\b': '\\b',
        784 '\f': '\\f',
        785 '\n': '\\n',
        786 '\r': '\\r',
        787 '\t': '\\t',
        788 '\x0B': '\\x0B', // '\v' is not supported in JScript
        789 '"': '\\"',
        790 '\\': '\\\\'
        791};
        792
        793
        794/**
        795 * Character mappings used internally for goog.string.escapeChar.
        796 * @private
        797 * @type {Object}
        798 */
        799goog.string.jsEscapeCache_ = {
        800 '\'': '\\\''
        801};
        802
        803
        804/**
        805 * Encloses a string in double quotes and escapes characters so that the
        806 * string is a valid JS string.
        807 * @param {string} s The string to quote.
        808 * @return {string} A copy of {@code s} surrounded by double quotes.
        809 */
        810goog.string.quote = function(s) {
        811 s = String(s);
        812 if (s.quote) {
        813 return s.quote();
        814 } else {
        815 var sb = ['"'];
        816 for (var i = 0; i < s.length; i++) {
        817 var ch = s.charAt(i);
        818 var cc = ch.charCodeAt(0);
        819 sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
        820 ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
        821 }
        822 sb.push('"');
        823 return sb.join('');
        824 }
        825};
        826
        827
        828/**
        829 * Takes a string and returns the escaped string for that character.
        830 * @param {string} str The string to escape.
        831 * @return {string} An escaped string representing {@code str}.
        832 */
        833goog.string.escapeString = function(str) {
        834 var sb = [];
        835 for (var i = 0; i < str.length; i++) {
        836 sb[i] = goog.string.escapeChar(str.charAt(i));
        837 }
        838 return sb.join('');
        839};
        840
        841
        842/**
        843 * Takes a character and returns the escaped string for that character. For
        844 * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
        845 * @param {string} c The character to escape.
        846 * @return {string} An escaped string representing {@code c}.
        847 */
        848goog.string.escapeChar = function(c) {
        849 if (c in goog.string.jsEscapeCache_) {
        850 return goog.string.jsEscapeCache_[c];
        851 }
        852
        853 if (c in goog.string.specialEscapeChars_) {
        854 return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
        855 }
        856
        857 var rv = c;
        858 var cc = c.charCodeAt(0);
        859 if (cc > 31 && cc < 127) {
        860 rv = c;
        861 } else {
        862 // tab is 9 but handled above
        863 if (cc < 256) {
        864 rv = '\\x';
        865 if (cc < 16 || cc > 256) {
        866 rv += '0';
        867 }
        868 } else {
        869 rv = '\\u';
        870 if (cc < 4096) { // \u1000
        871 rv += '0';
        872 }
        873 }
        874 rv += cc.toString(16).toUpperCase();
        875 }
        876
        877 return goog.string.jsEscapeCache_[c] = rv;
        878};
        879
        880
        881/**
        882 * Takes a string and creates a map (Object) in which the keys are the
        883 * characters in the string. The value for the key is set to true. You can
        884 * then use goog.object.map or goog.array.map to change the values.
        885 * @param {string} s The string to build the map from.
        886 * @return {Object} The map of characters used.
        887 */
        888// TODO(arv): It seems like we should have a generic goog.array.toMap. But do
        889// we want a dependency on goog.array in goog.string?
        890goog.string.toMap = function(s) {
        891 var rv = {};
        892 for (var i = 0; i < s.length; i++) {
        893 rv[s.charAt(i)] = true;
        894 }
        895 return rv;
        896};
        897
        898
        899/**
        900 * Checks whether a string contains a given substring.
        901 * @param {string} s The string to test.
        902 * @param {string} ss The substring to test for.
        903 * @return {boolean} True if {@code s} contains {@code ss}.
        904 */
        905goog.string.contains = function(s, ss) {
        906 return s.indexOf(ss) != -1;
        907};
        908
        909
        910/**
        911 * Returns the non-overlapping occurrences of ss in s.
        912 * If either s or ss evalutes to false, then returns zero.
        913 * @param {string} s The string to look in.
        914 * @param {string} ss The string to look for.
        915 * @return {number} Number of occurrences of ss in s.
        916 */
        917goog.string.countOf = function(s, ss) {
        918 return s && ss ? s.split(ss).length - 1 : 0;
        919};
        920
        921
        922/**
        923 * Removes a substring of a specified length at a specific
        924 * index in a string.
        925 * @param {string} s The base string from which to remove.
        926 * @param {number} index The index at which to remove the substring.
        927 * @param {number} stringLength The length of the substring to remove.
        928 * @return {string} A copy of {@code s} with the substring removed or the full
        929 * string if nothing is removed or the input is invalid.
        930 */
        931goog.string.removeAt = function(s, index, stringLength) {
        932 var resultStr = s;
        933 // If the index is greater or equal to 0 then remove substring
        934 if (index >= 0 && index < s.length && stringLength > 0) {
        935 resultStr = s.substr(0, index) +
        936 s.substr(index + stringLength, s.length - index - stringLength);
        937 }
        938 return resultStr;
        939};
        940
        941
        942/**
        943 * Removes the first occurrence of a substring from a string.
        944 * @param {string} s The base string from which to remove.
        945 * @param {string} ss The string to remove.
        946 * @return {string} A copy of {@code s} with {@code ss} removed or the full
        947 * string if nothing is removed.
        948 */
        949goog.string.remove = function(s, ss) {
        950 var re = new RegExp(goog.string.regExpEscape(ss), '');
        951 return s.replace(re, '');
        952};
        953
        954
        955/**
        956 * Removes all occurrences of a substring from a string.
        957 * @param {string} s The base string from which to remove.
        958 * @param {string} ss The string to remove.
        959 * @return {string} A copy of {@code s} with {@code ss} removed or the full
        960 * string if nothing is removed.
        961 */
        962goog.string.removeAll = function(s, ss) {
        963 var re = new RegExp(goog.string.regExpEscape(ss), 'g');
        964 return s.replace(re, '');
        965};
        966
        967
        968/**
        969 * Escapes characters in the string that are not safe to use in a RegExp.
        970 * @param {*} s The string to escape. If not a string, it will be casted
        971 * to one.
        972 * @return {string} A RegExp safe, escaped copy of {@code s}.
        973 */
        974goog.string.regExpEscape = function(s) {
        975 return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
        976 replace(/\x08/g, '\\x08');
        977};
        978
        979
        980/**
        981 * Repeats a string n times.
        982 * @param {string} string The string to repeat.
        983 * @param {number} length The number of times to repeat.
        984 * @return {string} A string containing {@code length} repetitions of
        985 * {@code string}.
        986 */
        987goog.string.repeat = function(string, length) {
        988 return new Array(length + 1).join(string);
        989};
        990
        991
        992/**
        993 * Pads number to given length and optionally rounds it to a given precision.
        994 * For example:
        995 * <pre>padNumber(1.25, 2, 3) -> '01.250'
        996 * padNumber(1.25, 2) -> '01.25'
        997 * padNumber(1.25, 2, 1) -> '01.3'
        998 * padNumber(1.25, 0) -> '1.25'</pre>
        999 *
        1000 * @param {number} num The number to pad.
        1001 * @param {number} length The desired length.
        1002 * @param {number=} opt_precision The desired precision.
        1003 * @return {string} {@code num} as a string with the given options.
        1004 */
        1005goog.string.padNumber = function(num, length, opt_precision) {
        1006 var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
        1007 var index = s.indexOf('.');
        1008 if (index == -1) {
        1009 index = s.length;
        1010 }
        1011 return goog.string.repeat('0', Math.max(0, length - index)) + s;
        1012};
        1013
        1014
        1015/**
        1016 * Returns a string representation of the given object, with
        1017 * null and undefined being returned as the empty string.
        1018 *
        1019 * @param {*} obj The object to convert.
        1020 * @return {string} A string representation of the {@code obj}.
        1021 */
        1022goog.string.makeSafe = function(obj) {
        1023 return obj == null ? '' : String(obj);
        1024};
        1025
        1026
        1027/**
        1028 * Concatenates string expressions. This is useful
        1029 * since some browsers are very inefficient when it comes to using plus to
        1030 * concat strings. Be careful when using null and undefined here since
        1031 * these will not be included in the result. If you need to represent these
        1032 * be sure to cast the argument to a String first.
        1033 * For example:
        1034 * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
        1035 * buildString(null, undefined) -> ''
        1036 * </pre>
        1037 * @param {...*} var_args A list of strings to concatenate. If not a string,
        1038 * it will be casted to one.
        1039 * @return {string} The concatenation of {@code var_args}.
        1040 */
        1041goog.string.buildString = function(var_args) {
        1042 return Array.prototype.join.call(arguments, '');
        1043};
        1044
        1045
        1046/**
        1047 * Returns a string with at least 64-bits of randomness.
        1048 *
        1049 * Doesn't trust Javascript's random function entirely. Uses a combination of
        1050 * random and current timestamp, and then encodes the string in base-36 to
        1051 * make it shorter.
        1052 *
        1053 * @return {string} A random string, e.g. sn1s7vb4gcic.
        1054 */
        1055goog.string.getRandomString = function() {
        1056 var x = 2147483648;
        1057 return Math.floor(Math.random() * x).toString(36) +
        1058 Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
        1059};
        1060
        1061
        1062/**
        1063 * Compares two version numbers.
        1064 *
        1065 * @param {string|number} version1 Version of first item.
        1066 * @param {string|number} version2 Version of second item.
        1067 *
        1068 * @return {number} 1 if {@code version1} is higher.
        1069 * 0 if arguments are equal.
        1070 * -1 if {@code version2} is higher.
        1071 */
        1072goog.string.compareVersions = function(version1, version2) {
        1073 var order = 0;
        1074 // Trim leading and trailing whitespace and split the versions into
        1075 // subversions.
        1076 var v1Subs = goog.string.trim(String(version1)).split('.');
        1077 var v2Subs = goog.string.trim(String(version2)).split('.');
        1078 var subCount = Math.max(v1Subs.length, v2Subs.length);
        1079
        1080 // Iterate over the subversions, as long as they appear to be equivalent.
        1081 for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
        1082 var v1Sub = v1Subs[subIdx] || '';
        1083 var v2Sub = v2Subs[subIdx] || '';
        1084
        1085 // Split the subversions into pairs of numbers and qualifiers (like 'b').
        1086 // Two different RegExp objects are needed because they are both using
        1087 // the 'g' flag.
        1088 var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g');
        1089 var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g');
        1090 do {
        1091 var v1Comp = v1CompParser.exec(v1Sub) || ['', '', ''];
        1092 var v2Comp = v2CompParser.exec(v2Sub) || ['', '', ''];
        1093 // Break if there are no more matches.
        1094 if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
        1095 break;
        1096 }
        1097
        1098 // Parse the numeric part of the subversion. A missing number is
        1099 // equivalent to 0.
        1100 var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
        1101 var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
        1102
        1103 // Compare the subversion components. The number has the highest
        1104 // precedence. Next, if the numbers are equal, a subversion without any
        1105 // qualifier is always higher than a subversion with any qualifier. Next,
        1106 // the qualifiers are compared as strings.
        1107 order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
        1108 goog.string.compareElements_(v1Comp[2].length == 0,
        1109 v2Comp[2].length == 0) ||
        1110 goog.string.compareElements_(v1Comp[2], v2Comp[2]);
        1111 // Stop as soon as an inequality is discovered.
        1112 } while (order == 0);
        1113 }
        1114
        1115 return order;
        1116};
        1117
        1118
        1119/**
        1120 * Compares elements of a version number.
        1121 *
        1122 * @param {string|number|boolean} left An element from a version number.
        1123 * @param {string|number|boolean} right An element from a version number.
        1124 *
        1125 * @return {number} 1 if {@code left} is higher.
        1126 * 0 if arguments are equal.
        1127 * -1 if {@code right} is higher.
        1128 * @private
        1129 */
        1130goog.string.compareElements_ = function(left, right) {
        1131 if (left < right) {
        1132 return -1;
        1133 } else if (left > right) {
        1134 return 1;
        1135 }
        1136 return 0;
        1137};
        1138
        1139
        1140/**
        1141 * Maximum value of #goog.string.hashCode, exclusive. 2^32.
        1142 * @type {number}
        1143 * @private
        1144 */
        1145goog.string.HASHCODE_MAX_ = 0x100000000;
        1146
        1147
        1148/**
        1149 * String hash function similar to java.lang.String.hashCode().
        1150 * The hash code for a string is computed as
        1151 * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
        1152 * where s[i] is the ith character of the string and n is the length of
        1153 * the string. We mod the result to make it between 0 (inclusive) and 2^32
        1154 * (exclusive).
        1155 * @param {string} str A string.
        1156 * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
        1157 * (exclusive). The empty string returns 0.
        1158 */
        1159goog.string.hashCode = function(str) {
        1160 var result = 0;
        1161 for (var i = 0; i < str.length; ++i) {
        1162 result = 31 * result + str.charCodeAt(i);
        1163 // Normalize to 4 byte range, 0 ... 2^32.
        1164 result %= goog.string.HASHCODE_MAX_;
        1165 }
        1166 return result;
        1167};
        1168
        1169
        1170/**
        1171 * The most recent unique ID. |0 is equivalent to Math.floor in this case.
        1172 * @type {number}
        1173 * @private
        1174 */
        1175goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
        1176
        1177
        1178/**
        1179 * Generates and returns a string which is unique in the current document.
        1180 * This is useful, for example, to create unique IDs for DOM elements.
        1181 * @return {string} A unique id.
        1182 */
        1183goog.string.createUniqueString = function() {
        1184 return 'goog_' + goog.string.uniqueStringCounter_++;
        1185};
        1186
        1187
        1188/**
        1189 * Converts the supplied string to a number, which may be Ininity or NaN.
        1190 * This function strips whitespace: (toNumber(' 123') === 123)
        1191 * This function accepts scientific notation: (toNumber('1e1') === 10)
        1192 *
        1193 * This is better than Javascript's built-in conversions because, sadly:
        1194 * (Number(' ') === 0) and (parseFloat('123a') === 123)
        1195 *
        1196 * @param {string} str The string to convert.
        1197 * @return {number} The number the supplied string represents, or NaN.
        1198 */
        1199goog.string.toNumber = function(str) {
        1200 var num = Number(str);
        1201 if (num == 0 && goog.string.isEmpty(str)) {
        1202 return NaN;
        1203 }
        1204 return num;
        1205};
        1206
        1207
        1208/**
        1209 * Returns whether the given string is lower camel case (e.g. "isFooBar").
        1210 *
        1211 * Note that this assumes the string is entirely letters.
        1212 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
        1213 *
        1214 * @param {string} str String to test.
        1215 * @return {boolean} Whether the string is lower camel case.
        1216 */
        1217goog.string.isLowerCamelCase = function(str) {
        1218 return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
        1219};
        1220
        1221
        1222/**
        1223 * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
        1224 *
        1225 * Note that this assumes the string is entirely letters.
        1226 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
        1227 *
        1228 * @param {string} str String to test.
        1229 * @return {boolean} Whether the string is upper camel case.
        1230 */
        1231goog.string.isUpperCamelCase = function(str) {
        1232 return /^([A-Z][a-z]*)+$/.test(str);
        1233};
        1234
        1235
        1236/**
        1237 * Converts a string from selector-case to camelCase (e.g. from
        1238 * "multi-part-string" to "multiPartString"), useful for converting
        1239 * CSS selectors and HTML dataset keys to their equivalent JS properties.
        1240 * @param {string} str The string in selector-case form.
        1241 * @return {string} The string in camelCase form.
        1242 */
        1243goog.string.toCamelCase = function(str) {
        1244 return String(str).replace(/\-([a-z])/g, function(all, match) {
        1245 return match.toUpperCase();
        1246 });
        1247};
        1248
        1249
        1250/**
        1251 * Converts a string from camelCase to selector-case (e.g. from
        1252 * "multiPartString" to "multi-part-string"), useful for converting JS
        1253 * style and dataset properties to equivalent CSS selectors and HTML keys.
        1254 * @param {string} str The string in camelCase form.
        1255 * @return {string} The string in selector-case form.
        1256 */
        1257goog.string.toSelectorCase = function(str) {
        1258 return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
        1259};
        1260
        1261
        1262/**
        1263 * Converts a string into TitleCase. First character of the string is always
        1264 * capitalized in addition to the first letter of every subsequent word.
        1265 * Words are delimited by one or more whitespaces by default. Custom delimiters
        1266 * can optionally be specified to replace the default, which doesn't preserve
        1267 * whitespace delimiters and instead must be explicitly included if needed.
        1268 *
        1269 * Default delimiter => " ":
        1270 * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'
        1271 * goog.string.toTitleCase('one two three') => 'One Two Three'
        1272 * goog.string.toTitleCase(' one two ') => ' One Two '
        1273 * goog.string.toTitleCase('one_two_three') => 'One_two_three'
        1274 * goog.string.toTitleCase('one-two-three') => 'One-two-three'
        1275 *
        1276 * Custom delimiter => "_-.":
        1277 * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'
        1278 * goog.string.toTitleCase('one two three', '_-.') => 'One two three'
        1279 * goog.string.toTitleCase(' one two ', '_-.') => ' one two '
        1280 * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'
        1281 * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'
        1282 * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
        1283 * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'
        1284 * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'
        1285 *
        1286 * @param {string} str String value in camelCase form.
        1287 * @param {string=} opt_delimiters Custom delimiter character set used to
        1288 * distinguish words in the string value. Each character represents a
        1289 * single delimiter. When provided, default whitespace delimiter is
        1290 * overridden and must be explicitly included if needed.
        1291 * @return {string} String value in TitleCase form.
        1292 */
        1293goog.string.toTitleCase = function(str, opt_delimiters) {
        1294 var delimiters = goog.isString(opt_delimiters) ?
        1295 goog.string.regExpEscape(opt_delimiters) : '\\s';
        1296
        1297 // For IE8, we need to prevent using an empty character set. Otherwise,
        1298 // incorrect matching will occur.
        1299 delimiters = delimiters ? '|[' + delimiters + ']+' : '';
        1300
        1301 var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
        1302 return str.replace(regexp, function(all, p1, p2) {
        1303 return p1 + p2.toUpperCase();
        1304 });
        1305};
        1306
        1307
        1308/**
        1309 * Parse a string in decimal or hexidecimal ('0xFFFF') form.
        1310 *
        1311 * To parse a particular radix, please use parseInt(string, radix) directly. See
        1312 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
        1313 *
        1314 * This is a wrapper for the built-in parseInt function that will only parse
        1315 * numbers as base 10 or base 16. Some JS implementations assume strings
        1316 * starting with "0" are intended to be octal. ES3 allowed but discouraged
        1317 * this behavior. ES5 forbids it. This function emulates the ES5 behavior.
        1318 *
        1319 * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
        1320 *
        1321 * @param {string|number|null|undefined} value The value to be parsed.
        1322 * @return {number} The number, parsed. If the string failed to parse, this
        1323 * will be NaN.
        1324 */
        1325goog.string.parseInt = function(value) {
        1326 // Force finite numbers to strings.
        1327 if (isFinite(value)) {
        1328 value = String(value);
        1329 }
        1330
        1331 if (goog.isString(value)) {
        1332 // If the string starts with '0x' or '-0x', parse as hex.
        1333 return /^\s*-?0x/i.test(value) ?
        1334 parseInt(value, 16) : parseInt(value, 10);
        1335 }
        1336
        1337 return NaN;
        1338};
        1339
        1340
        1341/**
        1342 * Splits a string on a separator a limited number of times.
        1343 *
        1344 * This implementation is more similar to Python or Java, where the limit
        1345 * parameter specifies the maximum number of splits rather than truncating
        1346 * the number of results.
        1347 *
        1348 * See http://docs.python.org/2/library/stdtypes.html#str.split
        1349 * See JavaDoc: http://goo.gl/F2AsY
        1350 * See Mozilla reference: http://goo.gl/dZdZs
        1351 *
        1352 * @param {string} str String to split.
        1353 * @param {string} separator The separator.
        1354 * @param {number} limit The limit to the number of splits. The resulting array
        1355 * will have a maximum length of limit+1. Negative numbers are the same
        1356 * as zero.
        1357 * @return {!Array.<string>} The string, split.
        1358 */
        1359
        1360goog.string.splitLimit = function(str, separator, limit) {
        1361 var parts = str.split(separator);
        1362 var returnVal = [];
        1363
        1364 // Only continue doing this while we haven't hit the limit and we have
        1365 // parts left.
        1366 while (limit > 0 && parts.length) {
        1367 returnVal.push(parts.shift());
        1368 limit--;
        1369 }
        1370
        1371 // If there are remaining parts, append them to the end.
        1372 if (parts.length) {
        1373 returnVal.push(parts.join(separator));
        1374 }
        1375
        1376 return returnVal;
        1377};
        1378
        \ No newline at end of file +string.js

        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 * @author arv@google.com (Erik Arvidsson)
        18 */
        19
        20
        21/**
        22 * Namespace for string utilities
        23 */
        24goog.provide('goog.string');
        25goog.provide('goog.string.Unicode');
        26
        27
        28/**
        29 * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps
        30 * with detection of double-escaping as this letter is frequently used.
        31 */
        32goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);
        33
        34
        35/**
        36 * @define {boolean} Whether to force non-dom html unescaping.
        37 */
        38goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false);
        39
        40
        41/**
        42 * Common Unicode string characters.
        43 * @enum {string}
        44 */
        45goog.string.Unicode = {
        46 NBSP: '\xa0'
        47};
        48
        49
        50/**
        51 * Fast prefix-checker.
        52 * @param {string} str The string to check.
        53 * @param {string} prefix A string to look for at the start of {@code str}.
        54 * @return {boolean} True if {@code str} begins with {@code prefix}.
        55 */
        56goog.string.startsWith = function(str, prefix) {
        57 return str.lastIndexOf(prefix, 0) == 0;
        58};
        59
        60
        61/**
        62 * Fast suffix-checker.
        63 * @param {string} str The string to check.
        64 * @param {string} suffix A string to look for at the end of {@code str}.
        65 * @return {boolean} True if {@code str} ends with {@code suffix}.
        66 */
        67goog.string.endsWith = function(str, suffix) {
        68 var l = str.length - suffix.length;
        69 return l >= 0 && str.indexOf(suffix, l) == l;
        70};
        71
        72
        73/**
        74 * Case-insensitive prefix-checker.
        75 * @param {string} str The string to check.
        76 * @param {string} prefix A string to look for at the end of {@code str}.
        77 * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring
        78 * case).
        79 */
        80goog.string.caseInsensitiveStartsWith = function(str, prefix) {
        81 return goog.string.caseInsensitiveCompare(
        82 prefix, str.substr(0, prefix.length)) == 0;
        83};
        84
        85
        86/**
        87 * Case-insensitive suffix-checker.
        88 * @param {string} str The string to check.
        89 * @param {string} suffix A string to look for at the end of {@code str}.
        90 * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring
        91 * case).
        92 */
        93goog.string.caseInsensitiveEndsWith = function(str, suffix) {
        94 return goog.string.caseInsensitiveCompare(
        95 suffix, str.substr(str.length - suffix.length, suffix.length)) == 0;
        96};
        97
        98
        99/**
        100 * Case-insensitive equality checker.
        101 * @param {string} str1 First string to check.
        102 * @param {string} str2 Second string to check.
        103 * @return {boolean} True if {@code str1} and {@code str2} are the same string,
        104 * ignoring case.
        105 */
        106goog.string.caseInsensitiveEquals = function(str1, str2) {
        107 return str1.toLowerCase() == str2.toLowerCase();
        108};
        109
        110
        111/**
        112 * Does simple python-style string substitution.
        113 * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
        114 * @param {string} str The string containing the pattern.
        115 * @param {...*} var_args The items to substitute into the pattern.
        116 * @return {string} A copy of {@code str} in which each occurrence of
        117 * {@code %s} has been replaced an argument from {@code var_args}.
        118 */
        119goog.string.subs = function(str, var_args) {
        120 var splitParts = str.split('%s');
        121 var returnString = '';
        122
        123 var subsArguments = Array.prototype.slice.call(arguments, 1);
        124 while (subsArguments.length &&
        125 // Replace up to the last split part. We are inserting in the
        126 // positions between split parts.
        127 splitParts.length > 1) {
        128 returnString += splitParts.shift() + subsArguments.shift();
        129 }
        130
        131 return returnString + splitParts.join('%s'); // Join unused '%s'
        132};
        133
        134
        135/**
        136 * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
        137 * and tabs) to a single space, and strips leading and trailing whitespace.
        138 * @param {string} str Input string.
        139 * @return {string} A copy of {@code str} with collapsed whitespace.
        140 */
        141goog.string.collapseWhitespace = function(str) {
        142 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        143 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        144 // include it in the regexp to enforce consistent cross-browser behavior.
        145 return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
        146};
        147
        148
        149/**
        150 * Checks if a string is empty or contains only whitespaces.
        151 * @param {string} str The string to check.
        152 * @return {boolean} Whether {@code str} is empty or whitespace only.
        153 */
        154goog.string.isEmptyOrWhitespace = function(str) {
        155 // testing length == 0 first is actually slower in all browsers (about the
        156 // same in Opera).
        157 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        158 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        159 // include it in the regexp to enforce consistent cross-browser behavior.
        160 return /^[\s\xa0]*$/.test(str);
        161};
        162
        163
        164/**
        165 * Checks if a string is empty.
        166 * @param {string} str The string to check.
        167 * @return {boolean} Whether {@code str} is empty.
        168 */
        169goog.string.isEmptyString = function(str) {
        170 return str.length == 0;
        171};
        172
        173
        174/**
        175 * Checks if a string is empty or contains only whitespaces.
        176 *
        177 * TODO(user): Deprecate this when clients have been switched over to
        178 * goog.string.isEmptyOrWhitespace.
        179 *
        180 * @param {string} str The string to check.
        181 * @return {boolean} Whether {@code str} is empty or whitespace only.
        182 */
        183goog.string.isEmpty = goog.string.isEmptyOrWhitespace;
        184
        185
        186/**
        187 * Checks if a string is null, undefined, empty or contains only whitespaces.
        188 * @param {*} str The string to check.
        189 * @return {boolean} Whether {@code str} is null, undefined, empty, or
        190 * whitespace only.
        191 * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str))
        192 * instead.
        193 */
        194goog.string.isEmptyOrWhitespaceSafe = function(str) {
        195 return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str));
        196};
        197
        198
        199/**
        200 * Checks if a string is null, undefined, empty or contains only whitespaces.
        201 *
        202 * TODO(user): Deprecate this when clients have been switched over to
        203 * goog.string.isEmptyOrWhitespaceSafe.
        204 *
        205 * @param {*} str The string to check.
        206 * @return {boolean} Whether {@code str} is null, undefined, empty, or
        207 * whitespace only.
        208 */
        209goog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe;
        210
        211
        212/**
        213 * Checks if a string is all breaking whitespace.
        214 * @param {string} str The string to check.
        215 * @return {boolean} Whether the string is all breaking whitespace.
        216 */
        217goog.string.isBreakingWhitespace = function(str) {
        218 return !/[^\t\n\r ]/.test(str);
        219};
        220
        221
        222/**
        223 * Checks if a string contains all letters.
        224 * @param {string} str string to check.
        225 * @return {boolean} True if {@code str} consists entirely of letters.
        226 */
        227goog.string.isAlpha = function(str) {
        228 return !/[^a-zA-Z]/.test(str);
        229};
        230
        231
        232/**
        233 * Checks if a string contains only numbers.
        234 * @param {*} str string to check. If not a string, it will be
        235 * casted to one.
        236 * @return {boolean} True if {@code str} is numeric.
        237 */
        238goog.string.isNumeric = function(str) {
        239 return !/[^0-9]/.test(str);
        240};
        241
        242
        243/**
        244 * Checks if a string contains only numbers or letters.
        245 * @param {string} str string to check.
        246 * @return {boolean} True if {@code str} is alphanumeric.
        247 */
        248goog.string.isAlphaNumeric = function(str) {
        249 return !/[^a-zA-Z0-9]/.test(str);
        250};
        251
        252
        253/**
        254 * Checks if a character is a space character.
        255 * @param {string} ch Character to check.
        256 * @return {boolean} True if {@code ch} is a space.
        257 */
        258goog.string.isSpace = function(ch) {
        259 return ch == ' ';
        260};
        261
        262
        263/**
        264 * Checks if a character is a valid unicode character.
        265 * @param {string} ch Character to check.
        266 * @return {boolean} True if {@code ch} is a valid unicode character.
        267 */
        268goog.string.isUnicodeChar = function(ch) {
        269 return ch.length == 1 && ch >= ' ' && ch <= '~' ||
        270 ch >= '\u0080' && ch <= '\uFFFD';
        271};
        272
        273
        274/**
        275 * Takes a string and replaces newlines with a space. Multiple lines are
        276 * replaced with a single space.
        277 * @param {string} str The string from which to strip newlines.
        278 * @return {string} A copy of {@code str} stripped of newlines.
        279 */
        280goog.string.stripNewlines = function(str) {
        281 return str.replace(/(\r\n|\r|\n)+/g, ' ');
        282};
        283
        284
        285/**
        286 * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
        287 * @param {string} str The string to in which to canonicalize newlines.
        288 * @return {string} {@code str} A copy of {@code} with canonicalized newlines.
        289 */
        290goog.string.canonicalizeNewlines = function(str) {
        291 return str.replace(/(\r\n|\r|\n)/g, '\n');
        292};
        293
        294
        295/**
        296 * Normalizes whitespace in a string, replacing all whitespace chars with
        297 * a space.
        298 * @param {string} str The string in which to normalize whitespace.
        299 * @return {string} A copy of {@code str} with all whitespace normalized.
        300 */
        301goog.string.normalizeWhitespace = function(str) {
        302 return str.replace(/\xa0|\s/g, ' ');
        303};
        304
        305
        306/**
        307 * Normalizes spaces in a string, replacing all consecutive spaces and tabs
        308 * with a single space. Replaces non-breaking space with a space.
        309 * @param {string} str The string in which to normalize spaces.
        310 * @return {string} A copy of {@code str} with all consecutive spaces and tabs
        311 * replaced with a single space.
        312 */
        313goog.string.normalizeSpaces = function(str) {
        314 return str.replace(/\xa0|[ \t]+/g, ' ');
        315};
        316
        317
        318/**
        319 * Removes the breaking spaces from the left and right of the string and
        320 * collapses the sequences of breaking spaces in the middle into single spaces.
        321 * The original and the result strings render the same way in HTML.
        322 * @param {string} str A string in which to collapse spaces.
        323 * @return {string} Copy of the string with normalized breaking spaces.
        324 */
        325goog.string.collapseBreakingSpaces = function(str) {
        326 return str.replace(/[\t\r\n ]+/g, ' ').replace(
        327 /^[\t\r\n ]+|[\t\r\n ]+$/g, '');
        328};
        329
        330
        331/**
        332 * Trims white spaces to the left and right of a string.
        333 * @param {string} str The string to trim.
        334 * @return {string} A trimmed copy of {@code str}.
        335 */
        336goog.string.trim = (goog.TRUSTED_SITE && String.prototype.trim) ?
        337 function(str) {
        338 return str.trim();
        339 } :
        340 function(str) {
        341 // Since IE doesn't include non-breaking-space (0xa0) in their \s
        342 // character class (as required by section 7.2 of the ECMAScript spec),
        343 // we explicitly include it in the regexp to enforce consistent
        344 // cross-browser behavior.
        345 return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
        346 };
        347
        348
        349/**
        350 * Trims whitespaces at the left end of a string.
        351 * @param {string} str The string to left trim.
        352 * @return {string} A trimmed copy of {@code str}.
        353 */
        354goog.string.trimLeft = function(str) {
        355 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        356 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        357 // include it in the regexp to enforce consistent cross-browser behavior.
        358 return str.replace(/^[\s\xa0]+/, '');
        359};
        360
        361
        362/**
        363 * Trims whitespaces at the right end of a string.
        364 * @param {string} str The string to right trim.
        365 * @return {string} A trimmed copy of {@code str}.
        366 */
        367goog.string.trimRight = function(str) {
        368 // Since IE doesn't include non-breaking-space (0xa0) in their \s character
        369 // class (as required by section 7.2 of the ECMAScript spec), we explicitly
        370 // include it in the regexp to enforce consistent cross-browser behavior.
        371 return str.replace(/[\s\xa0]+$/, '');
        372};
        373
        374
        375/**
        376 * A string comparator that ignores case.
        377 * -1 = str1 less than str2
        378 * 0 = str1 equals str2
        379 * 1 = str1 greater than str2
        380 *
        381 * @param {string} str1 The string to compare.
        382 * @param {string} str2 The string to compare {@code str1} to.
        383 * @return {number} The comparator result, as described above.
        384 */
        385goog.string.caseInsensitiveCompare = function(str1, str2) {
        386 var test1 = String(str1).toLowerCase();
        387 var test2 = String(str2).toLowerCase();
        388
        389 if (test1 < test2) {
        390 return -1;
        391 } else if (test1 == test2) {
        392 return 0;
        393 } else {
        394 return 1;
        395 }
        396};
        397
        398
        399/**
        400 * Compares two strings interpreting their numeric substrings as numbers.
        401 *
        402 * @param {string} str1 First string.
        403 * @param {string} str2 Second string.
        404 * @param {!RegExp} tokenizerRegExp Splits a string into substrings of
        405 * non-negative integers, non-numeric characters and optionally fractional
        406 * numbers starting with a decimal point.
        407 * @return {number} Negative if str1 < str2, 0 is str1 == str2, positive if
        408 * str1 > str2.
        409 * @private
        410 */
        411goog.string.numberAwareCompare_ = function(str1, str2, tokenizerRegExp) {
        412 if (str1 == str2) {
        413 return 0;
        414 }
        415 if (!str1) {
        416 return -1;
        417 }
        418 if (!str2) {
        419 return 1;
        420 }
        421
        422 // Using match to split the entire string ahead of time turns out to be faster
        423 // for most inputs than using RegExp.exec or iterating over each character.
        424 var tokens1 = str1.toLowerCase().match(tokenizerRegExp);
        425 var tokens2 = str2.toLowerCase().match(tokenizerRegExp);
        426
        427 var count = Math.min(tokens1.length, tokens2.length);
        428
        429 for (var i = 0; i < count; i++) {
        430 var a = tokens1[i];
        431 var b = tokens2[i];
        432
        433 // Compare pairs of tokens, returning if one token sorts before the other.
        434 if (a != b) {
        435 // Only if both tokens are integers is a special comparison required.
        436 // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
        437 var num1 = parseInt(a, 10);
        438 if (!isNaN(num1)) {
        439 var num2 = parseInt(b, 10);
        440 if (!isNaN(num2) && num1 - num2) {
        441 return num1 - num2;
        442 }
        443 }
        444 return a < b ? -1 : 1;
        445 }
        446 }
        447
        448 // If one string is a substring of the other, the shorter string sorts first.
        449 if (tokens1.length != tokens2.length) {
        450 return tokens1.length - tokens2.length;
        451 }
        452
        453 // The two strings must be equivalent except for case (perfect equality is
        454 // tested at the head of the function.) Revert to default ASCII string
        455 // comparison to stabilize the sort.
        456 return str1 < str2 ? -1 : 1;
        457};
        458
        459
        460/**
        461 * String comparison function that handles non-negative integer numbers in a
        462 * way humans might expect. Using this function, the string 'File 2.jpg' sorts
        463 * before 'File 10.jpg', and 'Version 1.9' before 'Version 1.10'. The comparison
        464 * is mostly case-insensitive, though strings that are identical except for case
        465 * are sorted with the upper-case strings before lower-case.
        466 *
        467 * This comparison function is up to 50x slower than either the default or the
        468 * case-insensitive compare. It should not be used in time-critical code, but
        469 * should be fast enough to sort several hundred short strings (like filenames)
        470 * with a reasonable delay.
        471 *
        472 * @param {string} str1 The string to compare in a numerically sensitive way.
        473 * @param {string} str2 The string to compare {@code str1} to.
        474 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
        475 * 0 if str1 > str2.
        476 */
        477goog.string.intAwareCompare = function(str1, str2) {
        478 return goog.string.numberAwareCompare_(str1, str2, /\d+|\D+/g);
        479};
        480
        481
        482/**
        483 * String comparison function that handles non-negative integer and fractional
        484 * numbers in a way humans might expect. Using this function, the string
        485 * 'File 2.jpg' sorts before 'File 10.jpg', and '3.14' before '3.2'. Equivalent
        486 * to {@link goog.string.intAwareCompare} apart from the way how it interprets
        487 * dots.
        488 *
        489 * @param {string} str1 The string to compare in a numerically sensitive way.
        490 * @param {string} str2 The string to compare {@code str1} to.
        491 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
        492 * 0 if str1 > str2.
        493 */
        494goog.string.floatAwareCompare = function(str1, str2) {
        495 return goog.string.numberAwareCompare_(str1, str2, /\d+|\.\d+|\D+/g);
        496};
        497
        498
        499/**
        500 * Alias for {@link goog.string.floatAwareCompare}.
        501 *
        502 * @param {string} str1
        503 * @param {string} str2
        504 * @return {number}
        505 */
        506goog.string.numerateCompare = goog.string.floatAwareCompare;
        507
        508
        509/**
        510 * URL-encodes a string
        511 * @param {*} str The string to url-encode.
        512 * @return {string} An encoded copy of {@code str} that is safe for urls.
        513 * Note that '#', ':', and other characters used to delimit portions
        514 * of URLs *will* be encoded.
        515 */
        516goog.string.urlEncode = function(str) {
        517 return encodeURIComponent(String(str));
        518};
        519
        520
        521/**
        522 * URL-decodes the string. We need to specially handle '+'s because
        523 * the javascript library doesn't convert them to spaces.
        524 * @param {string} str The string to url decode.
        525 * @return {string} The decoded {@code str}.
        526 */
        527goog.string.urlDecode = function(str) {
        528 return decodeURIComponent(str.replace(/\+/g, ' '));
        529};
        530
        531
        532/**
        533 * Converts \n to <br>s or <br />s.
        534 * @param {string} str The string in which to convert newlines.
        535 * @param {boolean=} opt_xml Whether to use XML compatible tags.
        536 * @return {string} A copy of {@code str} with converted newlines.
        537 */
        538goog.string.newLineToBr = function(str, opt_xml) {
        539 return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
        540};
        541
        542
        543/**
        544 * Escapes double quote '"' and single quote '\'' characters in addition to
        545 * '&', '<', and '>' so that a string can be included in an HTML tag attribute
        546 * value within double or single quotes.
        547 *
        548 * It should be noted that > doesn't need to be escaped for the HTML or XML to
        549 * be valid, but it has been decided to escape it for consistency with other
        550 * implementations.
        551 *
        552 * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
        553 * lowercase letter "e".
        554 *
        555 * NOTE(user):
        556 * HtmlEscape is often called during the generation of large blocks of HTML.
        557 * Using statics for the regular expressions and strings is an optimization
        558 * that can more than half the amount of time IE spends in this function for
        559 * large apps, since strings and regexes both contribute to GC allocations.
        560 *
        561 * Testing for the presence of a character before escaping increases the number
        562 * of function calls, but actually provides a speed increase for the average
        563 * case -- since the average case often doesn't require the escaping of all 4
        564 * characters and indexOf() is much cheaper than replace().
        565 * The worst case does suffer slightly from the additional calls, therefore the
        566 * opt_isLikelyToContainHtmlChars option has been included for situations
        567 * where all 4 HTML entities are very likely to be present and need escaping.
        568 *
        569 * Some benchmarks (times tended to fluctuate +-0.05ms):
        570 * FireFox IE6
        571 * (no chars / average (mix of cases) / all 4 chars)
        572 * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
        573 * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
        574 * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
        575 *
        576 * An additional advantage of checking if replace actually needs to be called
        577 * is a reduction in the number of object allocations, so as the size of the
        578 * application grows the difference between the various methods would increase.
        579 *
        580 * @param {string} str string to be escaped.
        581 * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
        582 * if the character needs replacing - use this option if you expect each of
        583 * the characters to appear often. Leave false if you expect few html
        584 * characters to occur in your strings, such as if you are escaping HTML.
        585 * @return {string} An escaped copy of {@code str}.
        586 */
        587goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
        588
        589 if (opt_isLikelyToContainHtmlChars) {
        590 str = str.replace(goog.string.AMP_RE_, '&amp;')
        591 .replace(goog.string.LT_RE_, '&lt;')
        592 .replace(goog.string.GT_RE_, '&gt;')
        593 .replace(goog.string.QUOT_RE_, '&quot;')
        594 .replace(goog.string.SINGLE_QUOTE_RE_, '&#39;')
        595 .replace(goog.string.NULL_RE_, '&#0;');
        596 if (goog.string.DETECT_DOUBLE_ESCAPING) {
        597 str = str.replace(goog.string.E_RE_, '&#101;');
        598 }
        599 return str;
        600
        601 } else {
        602 // quick test helps in the case when there are no chars to replace, in
        603 // worst case this makes barely a difference to the time taken
        604 if (!goog.string.ALL_RE_.test(str)) return str;
        605
        606 // str.indexOf is faster than regex.test in this case
        607 if (str.indexOf('&') != -1) {
        608 str = str.replace(goog.string.AMP_RE_, '&amp;');
        609 }
        610 if (str.indexOf('<') != -1) {
        611 str = str.replace(goog.string.LT_RE_, '&lt;');
        612 }
        613 if (str.indexOf('>') != -1) {
        614 str = str.replace(goog.string.GT_RE_, '&gt;');
        615 }
        616 if (str.indexOf('"') != -1) {
        617 str = str.replace(goog.string.QUOT_RE_, '&quot;');
        618 }
        619 if (str.indexOf('\'') != -1) {
        620 str = str.replace(goog.string.SINGLE_QUOTE_RE_, '&#39;');
        621 }
        622 if (str.indexOf('\x00') != -1) {
        623 str = str.replace(goog.string.NULL_RE_, '&#0;');
        624 }
        625 if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) {
        626 str = str.replace(goog.string.E_RE_, '&#101;');
        627 }
        628 return str;
        629 }
        630};
        631
        632
        633/**
        634 * Regular expression that matches an ampersand, for use in escaping.
        635 * @const {!RegExp}
        636 * @private
        637 */
        638goog.string.AMP_RE_ = /&/g;
        639
        640
        641/**
        642 * Regular expression that matches a less than sign, for use in escaping.
        643 * @const {!RegExp}
        644 * @private
        645 */
        646goog.string.LT_RE_ = /</g;
        647
        648
        649/**
        650 * Regular expression that matches a greater than sign, for use in escaping.
        651 * @const {!RegExp}
        652 * @private
        653 */
        654goog.string.GT_RE_ = />/g;
        655
        656
        657/**
        658 * Regular expression that matches a double quote, for use in escaping.
        659 * @const {!RegExp}
        660 * @private
        661 */
        662goog.string.QUOT_RE_ = /"/g;
        663
        664
        665/**
        666 * Regular expression that matches a single quote, for use in escaping.
        667 * @const {!RegExp}
        668 * @private
        669 */
        670goog.string.SINGLE_QUOTE_RE_ = /'/g;
        671
        672
        673/**
        674 * Regular expression that matches null character, for use in escaping.
        675 * @const {!RegExp}
        676 * @private
        677 */
        678goog.string.NULL_RE_ = /\x00/g;
        679
        680
        681/**
        682 * Regular expression that matches a lowercase letter "e", for use in escaping.
        683 * @const {!RegExp}
        684 * @private
        685 */
        686goog.string.E_RE_ = /e/g;
        687
        688
        689/**
        690 * Regular expression that matches any character that needs to be escaped.
        691 * @const {!RegExp}
        692 * @private
        693 */
        694goog.string.ALL_RE_ = (goog.string.DETECT_DOUBLE_ESCAPING ?
        695 /[\x00&<>"'e]/ :
        696 /[\x00&<>"']/);
        697
        698
        699/**
        700 * Unescapes an HTML string.
        701 *
        702 * @param {string} str The string to unescape.
        703 * @return {string} An unescaped copy of {@code str}.
        704 */
        705goog.string.unescapeEntities = function(str) {
        706 if (goog.string.contains(str, '&')) {
        707 // We are careful not to use a DOM if we do not have one or we explicitly
        708 // requested non-DOM html unescaping.
        709 if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING &&
        710 'document' in goog.global) {
        711 return goog.string.unescapeEntitiesUsingDom_(str);
        712 } else {
        713 // Fall back on pure XML entities
        714 return goog.string.unescapePureXmlEntities_(str);
        715 }
        716 }
        717 return str;
        718};
        719
        720
        721/**
        722 * Unescapes a HTML string using the provided document.
        723 *
        724 * @param {string} str The string to unescape.
        725 * @param {!Document} document A document to use in escaping the string.
        726 * @return {string} An unescaped copy of {@code str}.
        727 */
        728goog.string.unescapeEntitiesWithDocument = function(str, document) {
        729 if (goog.string.contains(str, '&')) {
        730 return goog.string.unescapeEntitiesUsingDom_(str, document);
        731 }
        732 return str;
        733};
        734
        735
        736/**
        737 * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
        738 * entities. This function is XSS-safe and whitespace-preserving.
        739 * @private
        740 * @param {string} str The string to unescape.
        741 * @param {Document=} opt_document An optional document to use for creating
        742 * elements. If this is not specified then the default window.document
        743 * will be used.
        744 * @return {string} The unescaped {@code str} string.
        745 */
        746goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
        747 /** @type {!Object<string, string>} */
        748 var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
        749 var div;
        750 if (opt_document) {
        751 div = opt_document.createElement('div');
        752 } else {
        753 div = goog.global.document.createElement('div');
        754 }
        755 // Match as many valid entity characters as possible. If the actual entity
        756 // happens to be shorter, it will still work as innerHTML will return the
        757 // trailing characters unchanged. Since the entity characters do not include
        758 // open angle bracket, there is no chance of XSS from the innerHTML use.
        759 // Since no whitespace is passed to innerHTML, whitespace is preserved.
        760 return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
        761 // Check for cached entity.
        762 var value = seen[s];
        763 if (value) {
        764 return value;
        765 }
        766 // Check for numeric entity.
        767 if (entity.charAt(0) == '#') {
        768 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
        769 var n = Number('0' + entity.substr(1));
        770 if (!isNaN(n)) {
        771 value = String.fromCharCode(n);
        772 }
        773 }
        774 // Fall back to innerHTML otherwise.
        775 if (!value) {
        776 // Append a non-entity character to avoid a bug in Webkit that parses
        777 // an invalid entity at the end of innerHTML text as the empty string.
        778 div.innerHTML = s + ' ';
        779 // Then remove the trailing character from the result.
        780 value = div.firstChild.nodeValue.slice(0, -1);
        781 }
        782 // Cache and return.
        783 return seen[s] = value;
        784 });
        785};
        786
        787
        788/**
        789 * Unescapes XML entities.
        790 * @private
        791 * @param {string} str The string to unescape.
        792 * @return {string} An unescaped copy of {@code str}.
        793 */
        794goog.string.unescapePureXmlEntities_ = function(str) {
        795 return str.replace(/&([^;]+);/g, function(s, entity) {
        796 switch (entity) {
        797 case 'amp':
        798 return '&';
        799 case 'lt':
        800 return '<';
        801 case 'gt':
        802 return '>';
        803 case 'quot':
        804 return '"';
        805 default:
        806 if (entity.charAt(0) == '#') {
        807 // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
        808 var n = Number('0' + entity.substr(1));
        809 if (!isNaN(n)) {
        810 return String.fromCharCode(n);
        811 }
        812 }
        813 // For invalid entities we just return the entity
        814 return s;
        815 }
        816 });
        817};
        818
        819
        820/**
        821 * Regular expression that matches an HTML entity.
        822 * See also HTML5: Tokenization / Tokenizing character references.
        823 * @private
        824 * @type {!RegExp}
        825 */
        826goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
        827
        828
        829/**
        830 * Do escaping of whitespace to preserve spatial formatting. We use character
        831 * entity #160 to make it safer for xml.
        832 * @param {string} str The string in which to escape whitespace.
        833 * @param {boolean=} opt_xml Whether to use XML compatible tags.
        834 * @return {string} An escaped copy of {@code str}.
        835 */
        836goog.string.whitespaceEscape = function(str, opt_xml) {
        837 // This doesn't use goog.string.preserveSpaces for backwards compatibility.
        838 return goog.string.newLineToBr(str.replace(/ /g, ' &#160;'), opt_xml);
        839};
        840
        841
        842/**
        843 * Preserve spaces that would be otherwise collapsed in HTML by replacing them
        844 * with non-breaking space Unicode characters.
        845 * @param {string} str The string in which to preserve whitespace.
        846 * @return {string} A copy of {@code str} with preserved whitespace.
        847 */
        848goog.string.preserveSpaces = function(str) {
        849 return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP);
        850};
        851
        852
        853/**
        854 * Strip quote characters around a string. The second argument is a string of
        855 * characters to treat as quotes. This can be a single character or a string of
        856 * multiple character and in that case each of those are treated as possible
        857 * quote characters. For example:
        858 *
        859 * <pre>
        860 * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
        861 * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
        862 * </pre>
        863 *
        864 * @param {string} str The string to strip.
        865 * @param {string} quoteChars The quote characters to strip.
        866 * @return {string} A copy of {@code str} without the quotes.
        867 */
        868goog.string.stripQuotes = function(str, quoteChars) {
        869 var length = quoteChars.length;
        870 for (var i = 0; i < length; i++) {
        871 var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i);
        872 if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) {
        873 return str.substring(1, str.length - 1);
        874 }
        875 }
        876 return str;
        877};
        878
        879
        880/**
        881 * Truncates a string to a certain length and adds '...' if necessary. The
        882 * length also accounts for the ellipsis, so a maximum length of 10 and a string
        883 * 'Hello World!' produces 'Hello W...'.
        884 * @param {string} str The string to truncate.
        885 * @param {number} chars Max number of characters.
        886 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
        887 * characters from being cut off in the middle.
        888 * @return {string} The truncated {@code str} string.
        889 */
        890goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) {
        891 if (opt_protectEscapedCharacters) {
        892 str = goog.string.unescapeEntities(str);
        893 }
        894
        895 if (str.length > chars) {
        896 str = str.substring(0, chars - 3) + '...';
        897 }
        898
        899 if (opt_protectEscapedCharacters) {
        900 str = goog.string.htmlEscape(str);
        901 }
        902
        903 return str;
        904};
        905
        906
        907/**
        908 * Truncate a string in the middle, adding "..." if necessary,
        909 * and favoring the beginning of the string.
        910 * @param {string} str The string to truncate the middle of.
        911 * @param {number} chars Max number of characters.
        912 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
        913 * characters from being cutoff in the middle.
        914 * @param {number=} opt_trailingChars Optional number of trailing characters to
        915 * leave at the end of the string, instead of truncating as close to the
        916 * middle as possible.
        917 * @return {string} A truncated copy of {@code str}.
        918 */
        919goog.string.truncateMiddle = function(str, chars,
        920 opt_protectEscapedCharacters, opt_trailingChars) {
        921 if (opt_protectEscapedCharacters) {
        922 str = goog.string.unescapeEntities(str);
        923 }
        924
        925 if (opt_trailingChars && str.length > chars) {
        926 if (opt_trailingChars > chars) {
        927 opt_trailingChars = chars;
        928 }
        929 var endPoint = str.length - opt_trailingChars;
        930 var startPoint = chars - opt_trailingChars;
        931 str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
        932 } else if (str.length > chars) {
        933 // Favor the beginning of the string:
        934 var half = Math.floor(chars / 2);
        935 var endPos = str.length - half;
        936 half += chars % 2;
        937 str = str.substring(0, half) + '...' + str.substring(endPos);
        938 }
        939
        940 if (opt_protectEscapedCharacters) {
        941 str = goog.string.htmlEscape(str);
        942 }
        943
        944 return str;
        945};
        946
        947
        948/**
        949 * Special chars that need to be escaped for goog.string.quote.
        950 * @private {!Object<string, string>}
        951 */
        952goog.string.specialEscapeChars_ = {
        953 '\0': '\\0',
        954 '\b': '\\b',
        955 '\f': '\\f',
        956 '\n': '\\n',
        957 '\r': '\\r',
        958 '\t': '\\t',
        959 '\x0B': '\\x0B', // '\v' is not supported in JScript
        960 '"': '\\"',
        961 '\\': '\\\\',
        962 // To support the use case of embedding quoted strings inside of <script>
        963 // tags, we have to make sure "<!--", "<script", and "</script" does not
        964 // appear in the resulting string. The specific strings that must be escaped
        965 // are documented at:
        966 // http://www.w3.org/TR/html51/semantics.html#restrictions-for-contents-of-script-elements
        967 '<': '\x3c'
        968};
        969
        970
        971/**
        972 * Character mappings used internally for goog.string.escapeChar.
        973 * @private {!Object<string, string>}
        974 */
        975goog.string.jsEscapeCache_ = {
        976 '\'': '\\\''
        977};
        978
        979
        980/**
        981 * Encloses a string in double quotes and escapes characters so that the
        982 * string is a valid JS string. The resulting string is safe to embed in
        983 * <script> tags as "<" is escaped.
        984 * @param {string} s The string to quote.
        985 * @return {string} A copy of {@code s} surrounded by double quotes.
        986 */
        987goog.string.quote = function(s) {
        988 s = String(s);
        989 var sb = ['"'];
        990 for (var i = 0; i < s.length; i++) {
        991 var ch = s.charAt(i);
        992 var cc = ch.charCodeAt(0);
        993 sb[i + 1] = goog.string.specialEscapeChars_[ch] ||
        994 ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch));
        995 }
        996 sb.push('"');
        997 return sb.join('');
        998};
        999
        1000
        1001/**
        1002 * Takes a string and returns the escaped string for that character.
        1003 * @param {string} str The string to escape.
        1004 * @return {string} An escaped string representing {@code str}.
        1005 */
        1006goog.string.escapeString = function(str) {
        1007 var sb = [];
        1008 for (var i = 0; i < str.length; i++) {
        1009 sb[i] = goog.string.escapeChar(str.charAt(i));
        1010 }
        1011 return sb.join('');
        1012};
        1013
        1014
        1015/**
        1016 * Takes a character and returns the escaped string for that character. For
        1017 * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
        1018 * @param {string} c The character to escape.
        1019 * @return {string} An escaped string representing {@code c}.
        1020 */
        1021goog.string.escapeChar = function(c) {
        1022 if (c in goog.string.jsEscapeCache_) {
        1023 return goog.string.jsEscapeCache_[c];
        1024 }
        1025
        1026 if (c in goog.string.specialEscapeChars_) {
        1027 return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c];
        1028 }
        1029
        1030 var rv = c;
        1031 var cc = c.charCodeAt(0);
        1032 if (cc > 31 && cc < 127) {
        1033 rv = c;
        1034 } else {
        1035 // tab is 9 but handled above
        1036 if (cc < 256) {
        1037 rv = '\\x';
        1038 if (cc < 16 || cc > 256) {
        1039 rv += '0';
        1040 }
        1041 } else {
        1042 rv = '\\u';
        1043 if (cc < 4096) { // \u1000
        1044 rv += '0';
        1045 }
        1046 }
        1047 rv += cc.toString(16).toUpperCase();
        1048 }
        1049
        1050 return goog.string.jsEscapeCache_[c] = rv;
        1051};
        1052
        1053
        1054/**
        1055 * Determines whether a string contains a substring.
        1056 * @param {string} str The string to search.
        1057 * @param {string} subString The substring to search for.
        1058 * @return {boolean} Whether {@code str} contains {@code subString}.
        1059 */
        1060goog.string.contains = function(str, subString) {
        1061 return str.indexOf(subString) != -1;
        1062};
        1063
        1064
        1065/**
        1066 * Determines whether a string contains a substring, ignoring case.
        1067 * @param {string} str The string to search.
        1068 * @param {string} subString The substring to search for.
        1069 * @return {boolean} Whether {@code str} contains {@code subString}.
        1070 */
        1071goog.string.caseInsensitiveContains = function(str, subString) {
        1072 return goog.string.contains(str.toLowerCase(), subString.toLowerCase());
        1073};
        1074
        1075
        1076/**
        1077 * Returns the non-overlapping occurrences of ss in s.
        1078 * If either s or ss evalutes to false, then returns zero.
        1079 * @param {string} s The string to look in.
        1080 * @param {string} ss The string to look for.
        1081 * @return {number} Number of occurrences of ss in s.
        1082 */
        1083goog.string.countOf = function(s, ss) {
        1084 return s && ss ? s.split(ss).length - 1 : 0;
        1085};
        1086
        1087
        1088/**
        1089 * Removes a substring of a specified length at a specific
        1090 * index in a string.
        1091 * @param {string} s The base string from which to remove.
        1092 * @param {number} index The index at which to remove the substring.
        1093 * @param {number} stringLength The length of the substring to remove.
        1094 * @return {string} A copy of {@code s} with the substring removed or the full
        1095 * string if nothing is removed or the input is invalid.
        1096 */
        1097goog.string.removeAt = function(s, index, stringLength) {
        1098 var resultStr = s;
        1099 // If the index is greater or equal to 0 then remove substring
        1100 if (index >= 0 && index < s.length && stringLength > 0) {
        1101 resultStr = s.substr(0, index) +
        1102 s.substr(index + stringLength, s.length - index - stringLength);
        1103 }
        1104 return resultStr;
        1105};
        1106
        1107
        1108/**
        1109 * Removes the first occurrence of a substring from a string.
        1110 * @param {string} s The base string from which to remove.
        1111 * @param {string} ss The string to remove.
        1112 * @return {string} A copy of {@code s} with {@code ss} removed or the full
        1113 * string if nothing is removed.
        1114 */
        1115goog.string.remove = function(s, ss) {
        1116 var re = new RegExp(goog.string.regExpEscape(ss), '');
        1117 return s.replace(re, '');
        1118};
        1119
        1120
        1121/**
        1122 * Removes all occurrences of a substring from a string.
        1123 * @param {string} s The base string from which to remove.
        1124 * @param {string} ss The string to remove.
        1125 * @return {string} A copy of {@code s} with {@code ss} removed or the full
        1126 * string if nothing is removed.
        1127 */
        1128goog.string.removeAll = function(s, ss) {
        1129 var re = new RegExp(goog.string.regExpEscape(ss), 'g');
        1130 return s.replace(re, '');
        1131};
        1132
        1133
        1134/**
        1135 * Escapes characters in the string that are not safe to use in a RegExp.
        1136 * @param {*} s The string to escape. If not a string, it will be casted
        1137 * to one.
        1138 * @return {string} A RegExp safe, escaped copy of {@code s}.
        1139 */
        1140goog.string.regExpEscape = function(s) {
        1141 return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
        1142 replace(/\x08/g, '\\x08');
        1143};
        1144
        1145
        1146/**
        1147 * Repeats a string n times.
        1148 * @param {string} string The string to repeat.
        1149 * @param {number} length The number of times to repeat.
        1150 * @return {string} A string containing {@code length} repetitions of
        1151 * {@code string}.
        1152 */
        1153goog.string.repeat = (String.prototype.repeat) ?
        1154 function(string, length) {
        1155 // The native method is over 100 times faster than the alternative.
        1156 return string.repeat(length);
        1157 } :
        1158 function(string, length) {
        1159 return new Array(length + 1).join(string);
        1160 };
        1161
        1162
        1163/**
        1164 * Pads number to given length and optionally rounds it to a given precision.
        1165 * For example:
        1166 * <pre>padNumber(1.25, 2, 3) -> '01.250'
        1167 * padNumber(1.25, 2) -> '01.25'
        1168 * padNumber(1.25, 2, 1) -> '01.3'
        1169 * padNumber(1.25, 0) -> '1.25'</pre>
        1170 *
        1171 * @param {number} num The number to pad.
        1172 * @param {number} length The desired length.
        1173 * @param {number=} opt_precision The desired precision.
        1174 * @return {string} {@code num} as a string with the given options.
        1175 */
        1176goog.string.padNumber = function(num, length, opt_precision) {
        1177 var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num);
        1178 var index = s.indexOf('.');
        1179 if (index == -1) {
        1180 index = s.length;
        1181 }
        1182 return goog.string.repeat('0', Math.max(0, length - index)) + s;
        1183};
        1184
        1185
        1186/**
        1187 * Returns a string representation of the given object, with
        1188 * null and undefined being returned as the empty string.
        1189 *
        1190 * @param {*} obj The object to convert.
        1191 * @return {string} A string representation of the {@code obj}.
        1192 */
        1193goog.string.makeSafe = function(obj) {
        1194 return obj == null ? '' : String(obj);
        1195};
        1196
        1197
        1198/**
        1199 * Concatenates string expressions. This is useful
        1200 * since some browsers are very inefficient when it comes to using plus to
        1201 * concat strings. Be careful when using null and undefined here since
        1202 * these will not be included in the result. If you need to represent these
        1203 * be sure to cast the argument to a String first.
        1204 * For example:
        1205 * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
        1206 * buildString(null, undefined) -> ''
        1207 * </pre>
        1208 * @param {...*} var_args A list of strings to concatenate. If not a string,
        1209 * it will be casted to one.
        1210 * @return {string} The concatenation of {@code var_args}.
        1211 */
        1212goog.string.buildString = function(var_args) {
        1213 return Array.prototype.join.call(arguments, '');
        1214};
        1215
        1216
        1217/**
        1218 * Returns a string with at least 64-bits of randomness.
        1219 *
        1220 * Doesn't trust Javascript's random function entirely. Uses a combination of
        1221 * random and current timestamp, and then encodes the string in base-36 to
        1222 * make it shorter.
        1223 *
        1224 * @return {string} A random string, e.g. sn1s7vb4gcic.
        1225 */
        1226goog.string.getRandomString = function() {
        1227 var x = 2147483648;
        1228 return Math.floor(Math.random() * x).toString(36) +
        1229 Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
        1230};
        1231
        1232
        1233/**
        1234 * Compares two version numbers.
        1235 *
        1236 * @param {string|number} version1 Version of first item.
        1237 * @param {string|number} version2 Version of second item.
        1238 *
        1239 * @return {number} 1 if {@code version1} is higher.
        1240 * 0 if arguments are equal.
        1241 * -1 if {@code version2} is higher.
        1242 */
        1243goog.string.compareVersions = function(version1, version2) {
        1244 var order = 0;
        1245 // Trim leading and trailing whitespace and split the versions into
        1246 // subversions.
        1247 var v1Subs = goog.string.trim(String(version1)).split('.');
        1248 var v2Subs = goog.string.trim(String(version2)).split('.');
        1249 var subCount = Math.max(v1Subs.length, v2Subs.length);
        1250
        1251 // Iterate over the subversions, as long as they appear to be equivalent.
        1252 for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
        1253 var v1Sub = v1Subs[subIdx] || '';
        1254 var v2Sub = v2Subs[subIdx] || '';
        1255
        1256 // Split the subversions into pairs of numbers and qualifiers (like 'b').
        1257 // Two different RegExp objects are needed because they are both using
        1258 // the 'g' flag.
        1259 var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g');
        1260 var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g');
        1261 do {
        1262 var v1Comp = v1CompParser.exec(v1Sub) || ['', '', ''];
        1263 var v2Comp = v2CompParser.exec(v2Sub) || ['', '', ''];
        1264 // Break if there are no more matches.
        1265 if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
        1266 break;
        1267 }
        1268
        1269 // Parse the numeric part of the subversion. A missing number is
        1270 // equivalent to 0.
        1271 var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
        1272 var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
        1273
        1274 // Compare the subversion components. The number has the highest
        1275 // precedence. Next, if the numbers are equal, a subversion without any
        1276 // qualifier is always higher than a subversion with any qualifier. Next,
        1277 // the qualifiers are compared as strings.
        1278 order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
        1279 goog.string.compareElements_(v1Comp[2].length == 0,
        1280 v2Comp[2].length == 0) ||
        1281 goog.string.compareElements_(v1Comp[2], v2Comp[2]);
        1282 // Stop as soon as an inequality is discovered.
        1283 } while (order == 0);
        1284 }
        1285
        1286 return order;
        1287};
        1288
        1289
        1290/**
        1291 * Compares elements of a version number.
        1292 *
        1293 * @param {string|number|boolean} left An element from a version number.
        1294 * @param {string|number|boolean} right An element from a version number.
        1295 *
        1296 * @return {number} 1 if {@code left} is higher.
        1297 * 0 if arguments are equal.
        1298 * -1 if {@code right} is higher.
        1299 * @private
        1300 */
        1301goog.string.compareElements_ = function(left, right) {
        1302 if (left < right) {
        1303 return -1;
        1304 } else if (left > right) {
        1305 return 1;
        1306 }
        1307 return 0;
        1308};
        1309
        1310
        1311/**
        1312 * String hash function similar to java.lang.String.hashCode().
        1313 * The hash code for a string is computed as
        1314 * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
        1315 * where s[i] is the ith character of the string and n is the length of
        1316 * the string. We mod the result to make it between 0 (inclusive) and 2^32
        1317 * (exclusive).
        1318 * @param {string} str A string.
        1319 * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
        1320 * (exclusive). The empty string returns 0.
        1321 */
        1322goog.string.hashCode = function(str) {
        1323 var result = 0;
        1324 for (var i = 0; i < str.length; ++i) {
        1325 // Normalize to 4 byte range, 0 ... 2^32.
        1326 result = (31 * result + str.charCodeAt(i)) >>> 0;
        1327 }
        1328 return result;
        1329};
        1330
        1331
        1332/**
        1333 * The most recent unique ID. |0 is equivalent to Math.floor in this case.
        1334 * @type {number}
        1335 * @private
        1336 */
        1337goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
        1338
        1339
        1340/**
        1341 * Generates and returns a string which is unique in the current document.
        1342 * This is useful, for example, to create unique IDs for DOM elements.
        1343 * @return {string} A unique id.
        1344 */
        1345goog.string.createUniqueString = function() {
        1346 return 'goog_' + goog.string.uniqueStringCounter_++;
        1347};
        1348
        1349
        1350/**
        1351 * Converts the supplied string to a number, which may be Infinity or NaN.
        1352 * This function strips whitespace: (toNumber(' 123') === 123)
        1353 * This function accepts scientific notation: (toNumber('1e1') === 10)
        1354 *
        1355 * This is better than Javascript's built-in conversions because, sadly:
        1356 * (Number(' ') === 0) and (parseFloat('123a') === 123)
        1357 *
        1358 * @param {string} str The string to convert.
        1359 * @return {number} The number the supplied string represents, or NaN.
        1360 */
        1361goog.string.toNumber = function(str) {
        1362 var num = Number(str);
        1363 if (num == 0 && goog.string.isEmptyOrWhitespace(str)) {
        1364 return NaN;
        1365 }
        1366 return num;
        1367};
        1368
        1369
        1370/**
        1371 * Returns whether the given string is lower camel case (e.g. "isFooBar").
        1372 *
        1373 * Note that this assumes the string is entirely letters.
        1374 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
        1375 *
        1376 * @param {string} str String to test.
        1377 * @return {boolean} Whether the string is lower camel case.
        1378 */
        1379goog.string.isLowerCamelCase = function(str) {
        1380 return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
        1381};
        1382
        1383
        1384/**
        1385 * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
        1386 *
        1387 * Note that this assumes the string is entirely letters.
        1388 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
        1389 *
        1390 * @param {string} str String to test.
        1391 * @return {boolean} Whether the string is upper camel case.
        1392 */
        1393goog.string.isUpperCamelCase = function(str) {
        1394 return /^([A-Z][a-z]*)+$/.test(str);
        1395};
        1396
        1397
        1398/**
        1399 * Converts a string from selector-case to camelCase (e.g. from
        1400 * "multi-part-string" to "multiPartString"), useful for converting
        1401 * CSS selectors and HTML dataset keys to their equivalent JS properties.
        1402 * @param {string} str The string in selector-case form.
        1403 * @return {string} The string in camelCase form.
        1404 */
        1405goog.string.toCamelCase = function(str) {
        1406 return String(str).replace(/\-([a-z])/g, function(all, match) {
        1407 return match.toUpperCase();
        1408 });
        1409};
        1410
        1411
        1412/**
        1413 * Converts a string from camelCase to selector-case (e.g. from
        1414 * "multiPartString" to "multi-part-string"), useful for converting JS
        1415 * style and dataset properties to equivalent CSS selectors and HTML keys.
        1416 * @param {string} str The string in camelCase form.
        1417 * @return {string} The string in selector-case form.
        1418 */
        1419goog.string.toSelectorCase = function(str) {
        1420 return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
        1421};
        1422
        1423
        1424/**
        1425 * Converts a string into TitleCase. First character of the string is always
        1426 * capitalized in addition to the first letter of every subsequent word.
        1427 * Words are delimited by one or more whitespaces by default. Custom delimiters
        1428 * can optionally be specified to replace the default, which doesn't preserve
        1429 * whitespace delimiters and instead must be explicitly included if needed.
        1430 *
        1431 * Default delimiter => " ":
        1432 * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'
        1433 * goog.string.toTitleCase('one two three') => 'One Two Three'
        1434 * goog.string.toTitleCase(' one two ') => ' One Two '
        1435 * goog.string.toTitleCase('one_two_three') => 'One_two_three'
        1436 * goog.string.toTitleCase('one-two-three') => 'One-two-three'
        1437 *
        1438 * Custom delimiter => "_-.":
        1439 * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'
        1440 * goog.string.toTitleCase('one two three', '_-.') => 'One two three'
        1441 * goog.string.toTitleCase(' one two ', '_-.') => ' one two '
        1442 * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'
        1443 * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'
        1444 * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
        1445 * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'
        1446 * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'
        1447 *
        1448 * @param {string} str String value in camelCase form.
        1449 * @param {string=} opt_delimiters Custom delimiter character set used to
        1450 * distinguish words in the string value. Each character represents a
        1451 * single delimiter. When provided, default whitespace delimiter is
        1452 * overridden and must be explicitly included if needed.
        1453 * @return {string} String value in TitleCase form.
        1454 */
        1455goog.string.toTitleCase = function(str, opt_delimiters) {
        1456 var delimiters = goog.isString(opt_delimiters) ?
        1457 goog.string.regExpEscape(opt_delimiters) : '\\s';
        1458
        1459 // For IE8, we need to prevent using an empty character set. Otherwise,
        1460 // incorrect matching will occur.
        1461 delimiters = delimiters ? '|[' + delimiters + ']+' : '';
        1462
        1463 var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
        1464 return str.replace(regexp, function(all, p1, p2) {
        1465 return p1 + p2.toUpperCase();
        1466 });
        1467};
        1468
        1469
        1470/**
        1471 * Capitalizes a string, i.e. converts the first letter to uppercase
        1472 * and all other letters to lowercase, e.g.:
        1473 *
        1474 * goog.string.capitalize('one') => 'One'
        1475 * goog.string.capitalize('ONE') => 'One'
        1476 * goog.string.capitalize('one two') => 'One two'
        1477 *
        1478 * Note that this function does not trim initial whitespace.
        1479 *
        1480 * @param {string} str String value to capitalize.
        1481 * @return {string} String value with first letter in uppercase.
        1482 */
        1483goog.string.capitalize = function(str) {
        1484 return String(str.charAt(0)).toUpperCase() +
        1485 String(str.substr(1)).toLowerCase();
        1486};
        1487
        1488
        1489/**
        1490 * Parse a string in decimal or hexidecimal ('0xFFFF') form.
        1491 *
        1492 * To parse a particular radix, please use parseInt(string, radix) directly. See
        1493 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
        1494 *
        1495 * This is a wrapper for the built-in parseInt function that will only parse
        1496 * numbers as base 10 or base 16. Some JS implementations assume strings
        1497 * starting with "0" are intended to be octal. ES3 allowed but discouraged
        1498 * this behavior. ES5 forbids it. This function emulates the ES5 behavior.
        1499 *
        1500 * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
        1501 *
        1502 * @param {string|number|null|undefined} value The value to be parsed.
        1503 * @return {number} The number, parsed. If the string failed to parse, this
        1504 * will be NaN.
        1505 */
        1506goog.string.parseInt = function(value) {
        1507 // Force finite numbers to strings.
        1508 if (isFinite(value)) {
        1509 value = String(value);
        1510 }
        1511
        1512 if (goog.isString(value)) {
        1513 // If the string starts with '0x' or '-0x', parse as hex.
        1514 return /^\s*-?0x/i.test(value) ?
        1515 parseInt(value, 16) : parseInt(value, 10);
        1516 }
        1517
        1518 return NaN;
        1519};
        1520
        1521
        1522/**
        1523 * Splits a string on a separator a limited number of times.
        1524 *
        1525 * This implementation is more similar to Python or Java, where the limit
        1526 * parameter specifies the maximum number of splits rather than truncating
        1527 * the number of results.
        1528 *
        1529 * See http://docs.python.org/2/library/stdtypes.html#str.split
        1530 * See JavaDoc: http://goo.gl/F2AsY
        1531 * See Mozilla reference: http://goo.gl/dZdZs
        1532 *
        1533 * @param {string} str String to split.
        1534 * @param {string} separator The separator.
        1535 * @param {number} limit The limit to the number of splits. The resulting array
        1536 * will have a maximum length of limit+1. Negative numbers are the same
        1537 * as zero.
        1538 * @return {!Array<string>} The string, split.
        1539 */
        1540
        1541goog.string.splitLimit = function(str, separator, limit) {
        1542 var parts = str.split(separator);
        1543 var returnVal = [];
        1544
        1545 // Only continue doing this while we haven't hit the limit and we have
        1546 // parts left.
        1547 while (limit > 0 && parts.length) {
        1548 returnVal.push(parts.shift());
        1549 limit--;
        1550 }
        1551
        1552 // If there are remaining parts, append them to the end.
        1553 if (parts.length) {
        1554 returnVal.push(parts.join(separator));
        1555 }
        1556
        1557 return returnVal;
        1558};
        1559
        1560
        1561/**
        1562 * Computes the Levenshtein edit distance between two strings.
        1563 * @param {string} a
        1564 * @param {string} b
        1565 * @return {number} The edit distance between the two strings.
        1566 */
        1567goog.string.editDistance = function(a, b) {
        1568 var v0 = [];
        1569 var v1 = [];
        1570
        1571 if (a == b) {
        1572 return 0;
        1573 }
        1574
        1575 if (!a.length || !b.length) {
        1576 return Math.max(a.length, b.length);
        1577 }
        1578
        1579 for (var i = 0; i < b.length + 1; i++) {
        1580 v0[i] = i;
        1581 }
        1582
        1583 for (var i = 0; i < a.length; i++) {
        1584 v1[0] = i + 1;
        1585
        1586 for (var j = 0; j < b.length; j++) {
        1587 var cost = a[i] != b[j];
        1588 // Cost for the substring is the minimum of adding one character, removing
        1589 // one character, or a swap.
        1590 v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
        1591 }
        1592
        1593 for (var j = 0; j < v0.length; j++) {
        1594 v0[j] = v1[j];
        1595 }
        1596 }
        1597
        1598 return v1[b.length];
        1599};
        \ No newline at end of file diff --git a/docs/source/lib/goog/string/typedstring.js.src.html b/docs/source/lib/goog/string/typedstring.js.src.html new file mode 100644 index 0000000..55103fb --- /dev/null +++ b/docs/source/lib/goog/string/typedstring.js.src.html @@ -0,0 +1 @@ +typedstring.js

        lib/goog/string/typedstring.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
        15goog.provide('goog.string.TypedString');
        16
        17
        18
        19/**
        20 * Wrapper for strings that conform to a data type or language.
        21 *
        22 * Implementations of this interface are wrappers for strings, and typically
        23 * associate a type contract with the wrapped string. Concrete implementations
        24 * of this interface may choose to implement additional run-time type checking,
        25 * see for example {@code goog.html.SafeHtml}. If available, client code that
        26 * needs to ensure type membership of an object should use the type's function
        27 * to assert type membership, such as {@code goog.html.SafeHtml.unwrap}.
        28 * @interface
        29 */
        30goog.string.TypedString = function() {};
        31
        32
        33/**
        34 * Interface marker of the TypedString interface.
        35 *
        36 * This property can be used to determine at runtime whether or not an object
        37 * implements this interface. All implementations of this interface set this
        38 * property to {@code true}.
        39 * @type {boolean}
        40 */
        41goog.string.TypedString.prototype.implementsGoogStringTypedString;
        42
        43
        44/**
        45 * Retrieves this wrapped string's value.
        46 * @return {!string} The wrapped string's value.
        47 */
        48goog.string.TypedString.prototype.getTypedStringValue;
        \ No newline at end of file diff --git a/docs/source/lib/goog/structs/collection.js.src.html b/docs/source/lib/goog/structs/collection.js.src.html new file mode 100644 index 0000000..59ede41 --- /dev/null +++ b/docs/source/lib/goog/structs/collection.js.src.html @@ -0,0 +1 @@ +collection.js

        lib/goog/structs/collection.js

        1// Copyright 2011 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 Defines the collection interface.
        17 *
        18 * @author nnaze@google.com (Nathan Naze)
        19 */
        20
        21goog.provide('goog.structs.Collection');
        22
        23
        24
        25/**
        26 * An interface for a collection of values.
        27 * @interface
        28 * @template T
        29 */
        30goog.structs.Collection = function() {};
        31
        32
        33/**
        34 * @param {T} value Value to add to the collection.
        35 */
        36goog.structs.Collection.prototype.add;
        37
        38
        39/**
        40 * @param {T} value Value to remove from the collection.
        41 */
        42goog.structs.Collection.prototype.remove;
        43
        44
        45/**
        46 * @param {T} value Value to find in the collection.
        47 * @return {boolean} Whether the collection contains the specified value.
        48 */
        49goog.structs.Collection.prototype.contains;
        50
        51
        52/**
        53 * @return {number} The number of values stored in the collection.
        54 */
        55goog.structs.Collection.prototype.getCount;
        56
        \ No newline at end of file diff --git a/docs/source/lib/goog/structs/map.js.src.html b/docs/source/lib/goog/structs/map.js.src.html index c9ff1a7..3b86438 100644 --- a/docs/source/lib/goog/structs/map.js.src.html +++ b/docs/source/lib/goog/structs/map.js.src.html @@ -1 +1 @@ -map.js

        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
        29goog.provide('goog.structs.Map');
        30
        31goog.require('goog.iter.Iterator');
        32goog.require('goog.iter.StopIteration');
        33goog.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 */
        44goog.structs.Map = function(opt_map, var_args) {
        45
        46 /**
        47 * Underlying JS object used to implement the map.
        48 * @type {!Object}
        49 * @private
        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 * @type {!Array.<string>}
        65 * @private
        66 */
        67 this.keys_ = [];
        68
        69 var argLength = arguments.length;
        70
        71 if (argLength > 1) {
        72 if (argLength % 2) {
        73 throw Error('Uneven number of arguments');
        74 }
        75 for (var i = 0; i < argLength; i += 2) {
        76 this.set(arguments[i], arguments[i + 1]);
        77 }
        78 } else if (opt_map) {
        79 this.addAll(/** @type {Object} */ (opt_map));
        80 }
        81};
        82
        83
        84/**
        85 * The number of key value pairs in the map.
        86 * @private
        87 * @type {number}
        88 */
        89goog.structs.Map.prototype.count_ = 0;
        90
        91
        92/**
        93 * Version used to detect changes while iterating.
        94 * @private
        95 * @type {number}
        96 */
        97goog.structs.Map.prototype.version_ = 0;
        98
        99
        100/**
        101 * @return {number} The number of key-value pairs in the map.
        102 */
        103goog.structs.Map.prototype.getCount = function() {
        104 return this.count_;
        105};
        106
        107
        108/**
        109 * Returns the values of the map.
        110 * @return {!Array} The values in the map.
        111 */
        112goog.structs.Map.prototype.getValues = function() {
        113 this.cleanupKeysArray_();
        114
        115 var rv = [];
        116 for (var i = 0; i < this.keys_.length; i++) {
        117 var key = this.keys_[i];
        118 rv.push(this.map_[key]);
        119 }
        120 return rv;
        121};
        122
        123
        124/**
        125 * Returns the keys of the map.
        126 * @return {!Array.<string>} Array of string values.
        127 */
        128goog.structs.Map.prototype.getKeys = function() {
        129 this.cleanupKeysArray_();
        130 return /** @type {!Array.<string>} */ (this.keys_.concat());
        131};
        132
        133
        134/**
        135 * Whether the map contains the given key.
        136 * @param {*} key The key to check for.
        137 * @return {boolean} Whether the map contains the key.
        138 */
        139goog.structs.Map.prototype.containsKey = function(key) {
        140 return goog.structs.Map.hasKey_(this.map_, key);
        141};
        142
        143
        144/**
        145 * Whether the map contains the given value. This is O(n).
        146 * @param {*} val The value to check for.
        147 * @return {boolean} Whether the map contains the value.
        148 */
        149goog.structs.Map.prototype.containsValue = function(val) {
        150 for (var i = 0; i < this.keys_.length; i++) {
        151 var key = this.keys_[i];
        152 if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
        153 return true;
        154 }
        155 }
        156 return false;
        157};
        158
        159
        160/**
        161 * Whether this map is equal to the argument map.
        162 * @param {goog.structs.Map} otherMap The map against which to test equality.
        163 * @param {function(?, ?) : boolean=} opt_equalityFn Optional equality function
        164 * to test equality of values. If not specified, this will test whether
        165 * the values contained in each map are identical objects.
        166 * @return {boolean} Whether the maps are equal.
        167 */
        168goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
        169 if (this === otherMap) {
        170 return true;
        171 }
        172
        173 if (this.count_ != otherMap.getCount()) {
        174 return false;
        175 }
        176
        177 var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
        178
        179 this.cleanupKeysArray_();
        180 for (var key, i = 0; key = this.keys_[i]; i++) {
        181 if (!equalityFn(this.get(key), otherMap.get(key))) {
        182 return false;
        183 }
        184 }
        185
        186 return true;
        187};
        188
        189
        190/**
        191 * Default equality test for values.
        192 * @param {*} a The first value.
        193 * @param {*} b The second value.
        194 * @return {boolean} Whether a and b reference the same object.
        195 */
        196goog.structs.Map.defaultEquals = function(a, b) {
        197 return a === b;
        198};
        199
        200
        201/**
        202 * @return {boolean} Whether the map is empty.
        203 */
        204goog.structs.Map.prototype.isEmpty = function() {
        205 return this.count_ == 0;
        206};
        207
        208
        209/**
        210 * Removes all key-value pairs from the map.
        211 */
        212goog.structs.Map.prototype.clear = function() {
        213 this.map_ = {};
        214 this.keys_.length = 0;
        215 this.count_ = 0;
        216 this.version_ = 0;
        217};
        218
        219
        220/**
        221 * Removes a key-value pair based on the key. This is O(logN) amortized due to
        222 * updating the keys array whenever the count becomes half the size of the keys
        223 * in the keys array.
        224 * @param {*} key The key to remove.
        225 * @return {boolean} Whether object was removed.
        226 */
        227goog.structs.Map.prototype.remove = function(key) {
        228 if (goog.structs.Map.hasKey_(this.map_, key)) {
        229 delete this.map_[key];
        230 this.count_--;
        231 this.version_++;
        232
        233 // clean up the keys array if the threshhold is hit
        234 if (this.keys_.length > 2 * this.count_) {
        235 this.cleanupKeysArray_();
        236 }
        237
        238 return true;
        239 }
        240 return false;
        241};
        242
        243
        244/**
        245 * Cleans up the temp keys array by removing entries that are no longer in the
        246 * map.
        247 * @private
        248 */
        249goog.structs.Map.prototype.cleanupKeysArray_ = function() {
        250 if (this.count_ != this.keys_.length) {
        251 // First remove keys that are no longer in the map.
        252 var srcIndex = 0;
        253 var destIndex = 0;
        254 while (srcIndex < this.keys_.length) {
        255 var key = this.keys_[srcIndex];
        256 if (goog.structs.Map.hasKey_(this.map_, key)) {
        257 this.keys_[destIndex++] = key;
        258 }
        259 srcIndex++;
        260 }
        261 this.keys_.length = destIndex;
        262 }
        263
        264 if (this.count_ != this.keys_.length) {
        265 // If the count still isn't correct, that means we have duplicates. This can
        266 // happen when the same key is added and removed multiple times. Now we have
        267 // to allocate one extra Object to remove the duplicates. This could have
        268 // been done in the first pass, but in the common case, we can avoid
        269 // allocating an extra object by only doing this when necessary.
        270 var seen = {};
        271 var srcIndex = 0;
        272 var destIndex = 0;
        273 while (srcIndex < this.keys_.length) {
        274 var key = this.keys_[srcIndex];
        275 if (!(goog.structs.Map.hasKey_(seen, key))) {
        276 this.keys_[destIndex++] = key;
        277 seen[key] = 1;
        278 }
        279 srcIndex++;
        280 }
        281 this.keys_.length = destIndex;
        282 }
        283};
        284
        285
        286/**
        287 * Returns the value for the given key. If the key is not found and the default
        288 * value is not given this will return {@code undefined}.
        289 * @param {*} key The key to get the value for.
        290 * @param {*=} opt_val The value to return if no item is found for the given
        291 * key, defaults to undefined.
        292 * @return {*} The value for the given key.
        293 */
        294goog.structs.Map.prototype.get = function(key, opt_val) {
        295 if (goog.structs.Map.hasKey_(this.map_, key)) {
        296 return this.map_[key];
        297 }
        298 return opt_val;
        299};
        300
        301
        302/**
        303 * Adds a key-value pair to the map.
        304 * @param {*} key The key.
        305 * @param {*} value The value to add.
        306 * @return {*} Some subclasses return a value.
        307 */
        308goog.structs.Map.prototype.set = function(key, value) {
        309 if (!(goog.structs.Map.hasKey_(this.map_, key))) {
        310 this.count_++;
        311 this.keys_.push(key);
        312 // Only change the version if we add a new key.
        313 this.version_++;
        314 }
        315 this.map_[key] = value;
        316};
        317
        318
        319/**
        320 * Adds multiple key-value pairs from another goog.structs.Map or Object.
        321 * @param {Object} map Object containing the data to add.
        322 */
        323goog.structs.Map.prototype.addAll = function(map) {
        324 var keys, values;
        325 if (map instanceof goog.structs.Map) {
        326 keys = map.getKeys();
        327 values = map.getValues();
        328 } else {
        329 keys = goog.object.getKeys(map);
        330 values = goog.object.getValues(map);
        331 }
        332 // we could use goog.array.forEach here but I don't want to introduce that
        333 // dependency just for this.
        334 for (var i = 0; i < keys.length; i++) {
        335 this.set(keys[i], values[i]);
        336 }
        337};
        338
        339
        340/**
        341 * Clones a map and returns a new map.
        342 * @return {!goog.structs.Map} A new map with the same key-value pairs.
        343 */
        344goog.structs.Map.prototype.clone = function() {
        345 return new goog.structs.Map(this);
        346};
        347
        348
        349/**
        350 * Returns a new map in which all the keys and values are interchanged
        351 * (keys become values and values become keys). If multiple keys map to the
        352 * same value, the chosen transposed value is implementation-dependent.
        353 *
        354 * It acts very similarly to {goog.object.transpose(Object)}.
        355 *
        356 * @return {!goog.structs.Map} The transposed map.
        357 */
        358goog.structs.Map.prototype.transpose = function() {
        359 var transposed = new goog.structs.Map();
        360 for (var i = 0; i < this.keys_.length; i++) {
        361 var key = this.keys_[i];
        362 var value = this.map_[key];
        363 transposed.set(value, key);
        364 }
        365
        366 return transposed;
        367};
        368
        369
        370/**
        371 * @return {!Object} Object representation of the map.
        372 */
        373goog.structs.Map.prototype.toObject = function() {
        374 this.cleanupKeysArray_();
        375 var obj = {};
        376 for (var i = 0; i < this.keys_.length; i++) {
        377 var key = this.keys_[i];
        378 obj[key] = this.map_[key];
        379 }
        380 return obj;
        381};
        382
        383
        384/**
        385 * Returns an iterator that iterates over the keys in the map. Removal of keys
        386 * while iterating might have undesired side effects.
        387 * @return {!goog.iter.Iterator} An iterator over the keys in the map.
        388 */
        389goog.structs.Map.prototype.getKeyIterator = function() {
        390 return this.__iterator__(true);
        391};
        392
        393
        394/**
        395 * Returns an iterator that iterates over the values in the map. Removal of
        396 * keys while iterating might have undesired side effects.
        397 * @return {!goog.iter.Iterator} An iterator over the values in the map.
        398 */
        399goog.structs.Map.prototype.getValueIterator = function() {
        400 return this.__iterator__(false);
        401};
        402
        403
        404/**
        405 * Returns an iterator that iterates over the values or the keys in the map.
        406 * This throws an exception if the map was mutated since the iterator was
        407 * created.
        408 * @param {boolean=} opt_keys True to iterate over the keys. False to iterate
        409 * over the values. The default value is false.
        410 * @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
        411 */
        412goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
        413 // Clean up keys to minimize the risk of iterating over dead keys.
        414 this.cleanupKeysArray_();
        415
        416 var i = 0;
        417 var keys = this.keys_;
        418 var map = this.map_;
        419 var version = this.version_;
        420 var selfObj = this;
        421
        422 var newIter = new goog.iter.Iterator;
        423 newIter.next = function() {
        424 while (true) {
        425 if (version != selfObj.version_) {
        426 throw Error('The map has changed since the iterator was created');
        427 }
        428 if (i >= keys.length) {
        429 throw goog.iter.StopIteration;
        430 }
        431 var key = keys[i++];
        432 return opt_keys ? key : map[key];
        433 }
        434 };
        435 return newIter;
        436};
        437
        438
        439/**
        440 * Safe way to test for hasOwnProperty. It even allows testing for
        441 * 'hasOwnProperty'.
        442 * @param {Object} obj The object to test for presence of the given key.
        443 * @param {*} key The key to check for.
        444 * @return {boolean} Whether the object has the key.
        445 * @private
        446 */
        447goog.structs.Map.hasKey_ = function(obj, key) {
        448 return Object.prototype.hasOwnProperty.call(obj, key);
        449};
        \ No newline at end of file +map.js

        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 *
        20 * This file contains an implementation of a Map structure. It implements a lot
        21 * of the methods used in goog.structs so those functions work on hashes. This
        22 * is best suited for complex key types. For simple keys such as numbers and
        23 * strings consider using the lighter-weight utilities in goog.object.
        24 */
        25
        26
        27goog.provide('goog.structs.Map');
        28
        29goog.require('goog.iter.Iterator');
        30goog.require('goog.iter.StopIteration');
        31goog.require('goog.object');
        32
        33
        34
        35/**
        36 * Class for Hash Map datastructure.
        37 * @param {*=} opt_map Map or Object to initialize the map with.
        38 * @param {...*} var_args If 2 or more arguments are present then they
        39 * will be used as key-value pairs.
        40 * @constructor
        41 * @template K, V
        42 */
        43goog.structs.Map = function(opt_map, var_args) {
        44
        45 /**
        46 * Underlying JS object used to implement the map.
        47 * @private {!Object}
        48 */
        49 this.map_ = {};
        50
        51 /**
        52 * An array of keys. This is necessary for two reasons:
        53 * 1. Iterating the keys using for (var key in this.map_) allocates an
        54 * object for every key in IE which is really bad for IE6 GC perf.
        55 * 2. Without a side data structure, we would need to escape all the keys
        56 * as that would be the only way we could tell during iteration if the
        57 * key was an internal key or a property of the object.
        58 *
        59 * This array can contain deleted keys so it's necessary to check the map
        60 * as well to see if the key is still in the map (this doesn't require a
        61 * memory allocation in IE).
        62 * @private {!Array<string>}
        63 */
        64 this.keys_ = [];
        65
        66 /**
        67 * The number of key value pairs in the map.
        68 * @private {number}
        69 */
        70 this.count_ = 0;
        71
        72 /**
        73 * Version used to detect changes while iterating.
        74 * @private {number}
        75 */
        76 this.version_ = 0;
        77
        78 var argLength = arguments.length;
        79
        80 if (argLength > 1) {
        81 if (argLength % 2) {
        82 throw Error('Uneven number of arguments');
        83 }
        84 for (var i = 0; i < argLength; i += 2) {
        85 this.set(arguments[i], arguments[i + 1]);
        86 }
        87 } else if (opt_map) {
        88 this.addAll(/** @type {Object} */ (opt_map));
        89 }
        90};
        91
        92
        93/**
        94 * @return {number} The number of key-value pairs in the map.
        95 */
        96goog.structs.Map.prototype.getCount = function() {
        97 return this.count_;
        98};
        99
        100
        101/**
        102 * Returns the values of the map.
        103 * @return {!Array<V>} The values in the map.
        104 */
        105goog.structs.Map.prototype.getValues = function() {
        106 this.cleanupKeysArray_();
        107
        108 var rv = [];
        109 for (var i = 0; i < this.keys_.length; i++) {
        110 var key = this.keys_[i];
        111 rv.push(this.map_[key]);
        112 }
        113 return rv;
        114};
        115
        116
        117/**
        118 * Returns the keys of the map.
        119 * @return {!Array<string>} Array of string values.
        120 */
        121goog.structs.Map.prototype.getKeys = function() {
        122 this.cleanupKeysArray_();
        123 return /** @type {!Array<string>} */ (this.keys_.concat());
        124};
        125
        126
        127/**
        128 * Whether the map contains the given key.
        129 * @param {*} key The key to check for.
        130 * @return {boolean} Whether the map contains the key.
        131 */
        132goog.structs.Map.prototype.containsKey = function(key) {
        133 return goog.structs.Map.hasKey_(this.map_, key);
        134};
        135
        136
        137/**
        138 * Whether the map contains the given value. This is O(n).
        139 * @param {V} val The value to check for.
        140 * @return {boolean} Whether the map contains the value.
        141 */
        142goog.structs.Map.prototype.containsValue = function(val) {
        143 for (var i = 0; i < this.keys_.length; i++) {
        144 var key = this.keys_[i];
        145 if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
        146 return true;
        147 }
        148 }
        149 return false;
        150};
        151
        152
        153/**
        154 * Whether this map is equal to the argument map.
        155 * @param {goog.structs.Map} otherMap The map against which to test equality.
        156 * @param {function(V, V): boolean=} opt_equalityFn Optional equality function
        157 * to test equality of values. If not specified, this will test whether
        158 * the values contained in each map are identical objects.
        159 * @return {boolean} Whether the maps are equal.
        160 */
        161goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
        162 if (this === otherMap) {
        163 return true;
        164 }
        165
        166 if (this.count_ != otherMap.getCount()) {
        167 return false;
        168 }
        169
        170 var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
        171
        172 this.cleanupKeysArray_();
        173 for (var key, i = 0; key = this.keys_[i]; i++) {
        174 if (!equalityFn(this.get(key), otherMap.get(key))) {
        175 return false;
        176 }
        177 }
        178
        179 return true;
        180};
        181
        182
        183/**
        184 * Default equality test for values.
        185 * @param {*} a The first value.
        186 * @param {*} b The second value.
        187 * @return {boolean} Whether a and b reference the same object.
        188 */
        189goog.structs.Map.defaultEquals = function(a, b) {
        190 return a === b;
        191};
        192
        193
        194/**
        195 * @return {boolean} Whether the map is empty.
        196 */
        197goog.structs.Map.prototype.isEmpty = function() {
        198 return this.count_ == 0;
        199};
        200
        201
        202/**
        203 * Removes all key-value pairs from the map.
        204 */
        205goog.structs.Map.prototype.clear = function() {
        206 this.map_ = {};
        207 this.keys_.length = 0;
        208 this.count_ = 0;
        209 this.version_ = 0;
        210};
        211
        212
        213/**
        214 * Removes a key-value pair based on the key. This is O(logN) amortized due to
        215 * updating the keys array whenever the count becomes half the size of the keys
        216 * in the keys array.
        217 * @param {*} key The key to remove.
        218 * @return {boolean} Whether object was removed.
        219 */
        220goog.structs.Map.prototype.remove = function(key) {
        221 if (goog.structs.Map.hasKey_(this.map_, key)) {
        222 delete this.map_[key];
        223 this.count_--;
        224 this.version_++;
        225
        226 // clean up the keys array if the threshhold is hit
        227 if (this.keys_.length > 2 * this.count_) {
        228 this.cleanupKeysArray_();
        229 }
        230
        231 return true;
        232 }
        233 return false;
        234};
        235
        236
        237/**
        238 * Cleans up the temp keys array by removing entries that are no longer in the
        239 * map.
        240 * @private
        241 */
        242goog.structs.Map.prototype.cleanupKeysArray_ = function() {
        243 if (this.count_ != this.keys_.length) {
        244 // First remove keys that are no longer in the map.
        245 var srcIndex = 0;
        246 var destIndex = 0;
        247 while (srcIndex < this.keys_.length) {
        248 var key = this.keys_[srcIndex];
        249 if (goog.structs.Map.hasKey_(this.map_, key)) {
        250 this.keys_[destIndex++] = key;
        251 }
        252 srcIndex++;
        253 }
        254 this.keys_.length = destIndex;
        255 }
        256
        257 if (this.count_ != this.keys_.length) {
        258 // If the count still isn't correct, that means we have duplicates. This can
        259 // happen when the same key is added and removed multiple times. Now we have
        260 // to allocate one extra Object to remove the duplicates. This could have
        261 // been done in the first pass, but in the common case, we can avoid
        262 // allocating an extra object by only doing this when necessary.
        263 var seen = {};
        264 var srcIndex = 0;
        265 var destIndex = 0;
        266 while (srcIndex < this.keys_.length) {
        267 var key = this.keys_[srcIndex];
        268 if (!(goog.structs.Map.hasKey_(seen, key))) {
        269 this.keys_[destIndex++] = key;
        270 seen[key] = 1;
        271 }
        272 srcIndex++;
        273 }
        274 this.keys_.length = destIndex;
        275 }
        276};
        277
        278
        279/**
        280 * Returns the value for the given key. If the key is not found and the default
        281 * value is not given this will return {@code undefined}.
        282 * @param {*} key The key to get the value for.
        283 * @param {DEFAULT=} opt_val The value to return if no item is found for the
        284 * given key, defaults to undefined.
        285 * @return {V|DEFAULT} The value for the given key.
        286 * @template DEFAULT
        287 */
        288goog.structs.Map.prototype.get = function(key, opt_val) {
        289 if (goog.structs.Map.hasKey_(this.map_, key)) {
        290 return this.map_[key];
        291 }
        292 return opt_val;
        293};
        294
        295
        296/**
        297 * Adds a key-value pair to the map.
        298 * @param {*} key The key.
        299 * @param {V} value The value to add.
        300 * @return {*} Some subclasses return a value.
        301 */
        302goog.structs.Map.prototype.set = function(key, value) {
        303 if (!(goog.structs.Map.hasKey_(this.map_, key))) {
        304 this.count_++;
        305 // TODO(johnlenz): This class lies, it claims to return an array of string
        306 // keys, but instead returns the original object used.
        307 this.keys_.push(/** @type {?} */ (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 */
        319goog.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 */
        342goog.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 */
        356goog.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 */
        370goog.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 */
        385goog.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 */
        401goog.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 */
        411goog.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 */
        424goog.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 version = this.version_;
        430 var selfObj = this;
        431
        432 var newIter = new goog.iter.Iterator;
        433 newIter.next = function() {
        434 if (version != selfObj.version_) {
        435 throw Error('The map has changed since the iterator was created');
        436 }
        437 if (i >= selfObj.keys_.length) {
        438 throw goog.iter.StopIteration;
        439 }
        440 var key = selfObj.keys_[i++];
        441 return opt_keys ? key : selfObj.map_[key];
        442 };
        443 return newIter;
        444};
        445
        446
        447/**
        448 * Safe way to test for hasOwnProperty. It even allows testing for
        449 * 'hasOwnProperty'.
        450 * @param {Object} obj The object to test for presence of the given key.
        451 * @param {*} key The key to check for.
        452 * @return {boolean} Whether the object has the key.
        453 * @private
        454 */
        455goog.structs.Map.hasKey_ = function(obj, key) {
        456 return Object.prototype.hasOwnProperty.call(obj, key);
        457};
        \ No newline at end of file diff --git a/docs/source/lib/goog/structs/set.js.src.html b/docs/source/lib/goog/structs/set.js.src.html new file mode 100644 index 0000000..5c0c4e3 --- /dev/null +++ b/docs/source/lib/goog/structs/set.js.src.html @@ -0,0 +1 @@ +set.js

        lib/goog/structs/set.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: Set.
        17 *
        18 * @author arv@google.com (Erik Arvidsson)
        19 *
        20 * This class implements a set data structure. Adding and removing is O(1). It
        21 * supports both object and primitive values. Be careful because you can add
        22 * both 1 and new Number(1), because these are not the same. You can even add
        23 * multiple new Number(1) because these are not equal.
        24 */
        25
        26
        27goog.provide('goog.structs.Set');
        28
        29goog.require('goog.structs');
        30goog.require('goog.structs.Collection');
        31goog.require('goog.structs.Map');
        32
        33
        34
        35/**
        36 * A set that can contain both primitives and objects. Adding and removing
        37 * elements is O(1). Primitives are treated as identical if they have the same
        38 * type and convert to the same string. Objects are treated as identical only
        39 * if they are references to the same object. WARNING: A goog.structs.Set can
        40 * contain both 1 and (new Number(1)), because they are not the same. WARNING:
        41 * Adding (new Number(1)) twice will yield two distinct elements, because they
        42 * are two different objects. WARNING: Any object that is added to a
        43 * goog.structs.Set will be modified! Because goog.getUid() is used to
        44 * identify objects, every object in the set will be mutated.
        45 * @param {Array<T>|Object<?,T>=} opt_values Initial values to start with.
        46 * @constructor
        47 * @implements {goog.structs.Collection<T>}
        48 * @final
        49 * @template T
        50 */
        51goog.structs.Set = function(opt_values) {
        52 this.map_ = new goog.structs.Map;
        53 if (opt_values) {
        54 this.addAll(opt_values);
        55 }
        56};
        57
        58
        59/**
        60 * Obtains a unique key for an element of the set. Primitives will yield the
        61 * same key if they have the same type and convert to the same string. Object
        62 * references will yield the same key only if they refer to the same object.
        63 * @param {*} val Object or primitive value to get a key for.
        64 * @return {string} A unique key for this value/object.
        65 * @private
        66 */
        67goog.structs.Set.getKey_ = function(val) {
        68 var type = typeof val;
        69 if (type == 'object' && val || type == 'function') {
        70 return 'o' + goog.getUid(/** @type {Object} */ (val));
        71 } else {
        72 return type.substr(0, 1) + val;
        73 }
        74};
        75
        76
        77/**
        78 * @return {number} The number of elements in the set.
        79 * @override
        80 */
        81goog.structs.Set.prototype.getCount = function() {
        82 return this.map_.getCount();
        83};
        84
        85
        86/**
        87 * Add a primitive or an object to the set.
        88 * @param {T} element The primitive or object to add.
        89 * @override
        90 */
        91goog.structs.Set.prototype.add = function(element) {
        92 this.map_.set(goog.structs.Set.getKey_(element), element);
        93};
        94
        95
        96/**
        97 * Adds all the values in the given collection to this set.
        98 * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
        99 * containing the elements to add.
        100 */
        101goog.structs.Set.prototype.addAll = function(col) {
        102 var values = goog.structs.getValues(col);
        103 var l = values.length;
        104 for (var i = 0; i < l; i++) {
        105 this.add(values[i]);
        106 }
        107};
        108
        109
        110/**
        111 * Removes all values in the given collection from this set.
        112 * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
        113 * containing the elements to remove.
        114 */
        115goog.structs.Set.prototype.removeAll = function(col) {
        116 var values = goog.structs.getValues(col);
        117 var l = values.length;
        118 for (var i = 0; i < l; i++) {
        119 this.remove(values[i]);
        120 }
        121};
        122
        123
        124/**
        125 * Removes the given element from this set.
        126 * @param {T} element The primitive or object to remove.
        127 * @return {boolean} Whether the element was found and removed.
        128 * @override
        129 */
        130goog.structs.Set.prototype.remove = function(element) {
        131 return this.map_.remove(goog.structs.Set.getKey_(element));
        132};
        133
        134
        135/**
        136 * Removes all elements from this set.
        137 */
        138goog.structs.Set.prototype.clear = function() {
        139 this.map_.clear();
        140};
        141
        142
        143/**
        144 * Tests whether this set is empty.
        145 * @return {boolean} True if there are no elements in this set.
        146 */
        147goog.structs.Set.prototype.isEmpty = function() {
        148 return this.map_.isEmpty();
        149};
        150
        151
        152/**
        153 * Tests whether this set contains the given element.
        154 * @param {T} element The primitive or object to test for.
        155 * @return {boolean} True if this set contains the given element.
        156 * @override
        157 */
        158goog.structs.Set.prototype.contains = function(element) {
        159 return this.map_.containsKey(goog.structs.Set.getKey_(element));
        160};
        161
        162
        163/**
        164 * Tests whether this set contains all the values in a given collection.
        165 * Repeated elements in the collection are ignored, e.g. (new
        166 * goog.structs.Set([1, 2])).containsAll([1, 1]) is True.
        167 * @param {goog.structs.Collection<T>|Object} col A collection-like object.
        168 * @return {boolean} True if the set contains all elements.
        169 */
        170goog.structs.Set.prototype.containsAll = function(col) {
        171 return goog.structs.every(col, this.contains, this);
        172};
        173
        174
        175/**
        176 * Finds all values that are present in both this set and the given collection.
        177 * @param {Array<S>|Object<?,S>} col A collection.
        178 * @return {!goog.structs.Set<T|S>} A new set containing all the values
        179 * (primitives or objects) present in both this set and the given
        180 * collection.
        181 * @template S
        182 */
        183goog.structs.Set.prototype.intersection = function(col) {
        184 var result = new goog.structs.Set();
        185
        186 var values = goog.structs.getValues(col);
        187 for (var i = 0; i < values.length; i++) {
        188 var value = values[i];
        189 if (this.contains(value)) {
        190 result.add(value);
        191 }
        192 }
        193
        194 return result;
        195};
        196
        197
        198/**
        199 * Finds all values that are present in this set and not in the given
        200 * collection.
        201 * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection.
        202 * @return {!goog.structs.Set} A new set containing all the values
        203 * (primitives or objects) present in this set but not in the given
        204 * collection.
        205 */
        206goog.structs.Set.prototype.difference = function(col) {
        207 var result = this.clone();
        208 result.removeAll(col);
        209 return result;
        210};
        211
        212
        213/**
        214 * Returns an array containing all the elements in this set.
        215 * @return {!Array<T>} An array containing all the elements in this set.
        216 */
        217goog.structs.Set.prototype.getValues = function() {
        218 return this.map_.getValues();
        219};
        220
        221
        222/**
        223 * Creates a shallow clone of this set.
        224 * @return {!goog.structs.Set<T>} A new set containing all the same elements as
        225 * this set.
        226 */
        227goog.structs.Set.prototype.clone = function() {
        228 return new goog.structs.Set(this);
        229};
        230
        231
        232/**
        233 * Tests whether the given collection consists of the same elements as this set,
        234 * regardless of order, without repetition. Primitives are treated as equal if
        235 * they have the same type and convert to the same string; objects are treated
        236 * as equal if they are references to the same object. This operation is O(n).
        237 * @param {goog.structs.Collection<T>|Object} col A collection.
        238 * @return {boolean} True if the given collection consists of the same elements
        239 * as this set, regardless of order, without repetition.
        240 */
        241goog.structs.Set.prototype.equals = function(col) {
        242 return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col);
        243};
        244
        245
        246/**
        247 * Tests whether the given collection contains all the elements in this set.
        248 * Primitives are treated as equal if they have the same type and convert to the
        249 * same string; objects are treated as equal if they are references to the same
        250 * object. This operation is O(n).
        251 * @param {goog.structs.Collection<T>|Object} col A collection.
        252 * @return {boolean} True if this set is a subset of the given collection.
        253 */
        254goog.structs.Set.prototype.isSubsetOf = function(col) {
        255 var colCount = goog.structs.getCount(col);
        256 if (this.getCount() > colCount) {
        257 return false;
        258 }
        259 // TODO(user) Find the minimal collection size where the conversion makes
        260 // the contains() method faster.
        261 if (!(col instanceof goog.structs.Set) && colCount > 5) {
        262 // Convert to a goog.structs.Set so that goog.structs.contains runs in
        263 // O(1) time instead of O(n) time.
        264 col = new goog.structs.Set(col);
        265 }
        266 return goog.structs.every(this, function(value) {
        267 return goog.structs.contains(col, value);
        268 });
        269};
        270
        271
        272/**
        273 * Returns an iterator that iterates over the elements in this set.
        274 * @param {boolean=} opt_keys This argument is ignored.
        275 * @return {!goog.iter.Iterator} An iterator over the elements in this set.
        276 */
        277goog.structs.Set.prototype.__iterator__ = function(opt_keys) {
        278 return this.map_.__iterator__(false);
        279};
        \ No newline at end of file diff --git a/docs/source/lib/goog/structs/structs.js.src.html b/docs/source/lib/goog/structs/structs.js.src.html index bdc37d2..787c1a2 100644 --- a/docs/source/lib/goog/structs/structs.js.src.html +++ b/docs/source/lib/goog/structs/structs.js.src.html @@ -1 +1 @@ -structs.js

        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
        26goog.provide('goog.structs');
        27
        28goog.require('goog.array');
        29goog.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 */
        41goog.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 */
        57goog.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 */
        82goog.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 */
        110goog.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 */
        129goog.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 */
        148goog.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 * @param {S} col The collection-like object.
        165 * @param {function(this:T,?,?,S):?} f The function to call for every value.
        166 * This function takes
        167 * 3 arguments (the value, the key or undefined if the collection has no
        168 * notion of keys, and the collection) and the return value is irrelevant.
        169 * @param {T=} opt_obj The object to be used as the value of 'this'
        170 * within {@code f}.
        171 * @template T,S
        172 */
        173goog.structs.forEach = function(col, f, opt_obj) {
        174 if (typeof col.forEach == 'function') {
        175 col.forEach(f, opt_obj);
        176 } else if (goog.isArrayLike(col) || goog.isString(col)) {
        177 goog.array.forEach(/** @type {Array} */ (col), f, opt_obj);
        178 } else {
        179 var keys = goog.structs.getKeys(col);
        180 var values = goog.structs.getValues(col);
        181 var l = values.length;
        182 for (var i = 0; i < l; i++) {
        183 f.call(opt_obj, values[i], keys && keys[i], col);
        184 }
        185 }
        186};
        187
        188
        189/**
        190 * Calls a function for every value in the collection. When a call returns true,
        191 * adds the value to a new collection (Array is returned by default).
        192 *
        193 * @param {S} col The collection-like object.
        194 * @param {function(this:T,?,?,S):boolean} f The function to call for every
        195 * value. This function takes
        196 * 3 arguments (the value, the key or undefined if the collection has no
        197 * notion of keys, and the collection) and should return a Boolean. If the
        198 * return value is true the value is added to the result collection. If it
        199 * is false the value is not included.
        200 * @param {T=} opt_obj The object to be used as the value of 'this'
        201 * within {@code f}.
        202 * @return {!Object|!Array} A new collection where the passed values are
        203 * present. If col is a key-less collection an array is returned. If col
        204 * has keys and values a plain old JS object is returned.
        205 * @template T,S
        206 */
        207goog.structs.filter = function(col, f, opt_obj) {
        208 if (typeof col.filter == 'function') {
        209 return col.filter(f, opt_obj);
        210 }
        211 if (goog.isArrayLike(col) || goog.isString(col)) {
        212 return goog.array.filter(/** @type {!Array} */ (col), f, opt_obj);
        213 }
        214
        215 var rv;
        216 var keys = goog.structs.getKeys(col);
        217 var values = goog.structs.getValues(col);
        218 var l = values.length;
        219 if (keys) {
        220 rv = {};
        221 for (var i = 0; i < l; i++) {
        222 if (f.call(opt_obj, values[i], keys[i], col)) {
        223 rv[keys[i]] = values[i];
        224 }
        225 }
        226 } else {
        227 // We should not use goog.array.filter here since we want to make sure that
        228 // the index is undefined as well as make sure that col is passed to the
        229 // function.
        230 rv = [];
        231 for (var i = 0; i < l; i++) {
        232 if (f.call(opt_obj, values[i], undefined, col)) {
        233 rv.push(values[i]);
        234 }
        235 }
        236 }
        237 return rv;
        238};
        239
        240
        241/**
        242 * Calls a function for every value in the collection and adds the result into a
        243 * new collection (defaults to creating a new Array).
        244 *
        245 * @param {S} col The collection-like object.
        246 * @param {function(this:T,?,?,S):V} f The function to call for every value.
        247 * This function takes 3 arguments (the value, the key or undefined if the
        248 * collection has no notion of keys, and the collection) and should return
        249 * something. The result will be used as the value in the new collection.
        250 * @param {T=} opt_obj The object to be used as the value of 'this'
        251 * within {@code f}.
        252 * @return {!Object.<V>|!Array.<V>} A new collection with the new values. If
        253 * col is a key-less collection an array is returned. If col has keys and
        254 * values a plain old JS object is returned.
        255 * @template T,S,V
        256 */
        257goog.structs.map = function(col, f, opt_obj) {
        258 if (typeof col.map == 'function') {
        259 return col.map(f, opt_obj);
        260 }
        261 if (goog.isArrayLike(col) || goog.isString(col)) {
        262 return goog.array.map(/** @type {!Array} */ (col), f, opt_obj);
        263 }
        264
        265 var rv;
        266 var keys = goog.structs.getKeys(col);
        267 var values = goog.structs.getValues(col);
        268 var l = values.length;
        269 if (keys) {
        270 rv = {};
        271 for (var i = 0; i < l; i++) {
        272 rv[keys[i]] = f.call(opt_obj, values[i], keys[i], col);
        273 }
        274 } else {
        275 // We should not use goog.array.map here since we want to make sure that
        276 // the index is undefined as well as make sure that col is passed to the
        277 // function.
        278 rv = [];
        279 for (var i = 0; i < l; i++) {
        280 rv[i] = f.call(opt_obj, values[i], undefined, col);
        281 }
        282 }
        283 return rv;
        284};
        285
        286
        287/**
        288 * Calls f for each value in a collection. If any call returns true this returns
        289 * true (without checking the rest). If all returns false this returns false.
        290 *
        291 * @param {S} col The collection-like object.
        292 * @param {function(this:T,?,?,S):boolean} f The function to call for every
        293 * value. This function takes 3 arguments (the value, the key or undefined
        294 * if the collection has no notion of keys, and the collection) and should
        295 * return a boolean.
        296 * @param {T=} opt_obj The object to be used as the value of 'this'
        297 * within {@code f}.
        298 * @return {boolean} True if any value passes the test.
        299 * @template T,S
        300 */
        301goog.structs.some = function(col, f, opt_obj) {
        302 if (typeof col.some == 'function') {
        303 return col.some(f, opt_obj);
        304 }
        305 if (goog.isArrayLike(col) || goog.isString(col)) {
        306 return goog.array.some(/** @type {!Array} */ (col), f, opt_obj);
        307 }
        308 var keys = goog.structs.getKeys(col);
        309 var values = goog.structs.getValues(col);
        310 var l = values.length;
        311 for (var i = 0; i < l; i++) {
        312 if (f.call(opt_obj, values[i], keys && keys[i], col)) {
        313 return true;
        314 }
        315 }
        316 return false;
        317};
        318
        319
        320/**
        321 * Calls f for each value in a collection. If all calls return true this return
        322 * true this returns true. If any returns false this returns false at this point
        323 * and does not continue to check the remaining values.
        324 *
        325 * @param {S} col The collection-like object.
        326 * @param {function(this:T,?,?,S):boolean} f The function to call for every
        327 * value. This function takes 3 arguments (the value, the key or
        328 * undefined if the collection has no notion of keys, and the collection)
        329 * and should return a boolean.
        330 * @param {T=} opt_obj The object to be used as the value of 'this'
        331 * within {@code f}.
        332 * @return {boolean} True if all key-value pairs pass the test.
        333 * @template T,S
        334 */
        335goog.structs.every = function(col, f, opt_obj) {
        336 if (typeof col.every == 'function') {
        337 return col.every(f, opt_obj);
        338 }
        339 if (goog.isArrayLike(col) || goog.isString(col)) {
        340 return goog.array.every(/** @type {!Array} */ (col), f, opt_obj);
        341 }
        342 var keys = goog.structs.getKeys(col);
        343 var values = goog.structs.getValues(col);
        344 var l = values.length;
        345 for (var i = 0; i < l; i++) {
        346 if (!f.call(opt_obj, values[i], keys && keys[i], col)) {
        347 return false;
        348 }
        349 }
        350 return true;
        351};
        \ No newline at end of file +structs.js

        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
        26goog.provide('goog.structs');
        27
        28goog.require('goog.array');
        29goog.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 */
        41goog.structs.getCount = function(col) {
        42 if (col.getCount && 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 */
        57goog.structs.getValues = function(col) {
        58 if (col.getValues && 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 */
        82goog.structs.getKeys = function(col) {
        83 if (col.getKeys && 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 (col.getValues && 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 */
        110goog.structs.contains = function(col, val) {
        111 if (col.contains && typeof col.contains == 'function') {
        112 return col.contains(val);
        113 }
        114 if (col.containsValue && 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 */
        129goog.structs.isEmpty = function(col) {
        130 if (col.isEmpty && typeof col.isEmpty == 'function') {
        131 return col.isEmpty();
        132 }
        133
        134 // We do not use goog.string.isEmptyOrWhitespace 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 */
        148goog.structs.clear = function(col) {
        149 // NOTE(arv): This should not contain strings because strings are immutable
        150 if (col.clear && 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 */
        176goog.structs.forEach = function(col, f, opt_obj) {
        177 if (col.forEach && 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 */
        210goog.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 */
        260goog.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 */
        304goog.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 */
        338goog.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};
        \ No newline at end of file diff --git a/docs/source/lib/goog/style/style.js.src.html b/docs/source/lib/goog/style/style.js.src.html new file mode 100644 index 0000000..854a738 --- /dev/null +++ b/docs/source/lib/goog/style/style.js.src.html @@ -0,0 +1 @@ +style.js

        lib/goog/style/style.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 element styles.
        17 *
        18 * @author arv@google.com (Erik Arvidsson)
        19 * @author eae@google.com (Emil A Eklund)
        20 * @see ../demos/inline_block_quirks.html
        21 * @see ../demos/inline_block_standards.html
        22 * @see ../demos/style_viewport.html
        23 */
        24
        25goog.provide('goog.style');
        26
        27
        28goog.require('goog.array');
        29goog.require('goog.asserts');
        30goog.require('goog.dom');
        31goog.require('goog.dom.NodeType');
        32goog.require('goog.dom.TagName');
        33goog.require('goog.dom.vendor');
        34goog.require('goog.math.Box');
        35goog.require('goog.math.Coordinate');
        36goog.require('goog.math.Rect');
        37goog.require('goog.math.Size');
        38goog.require('goog.object');
        39goog.require('goog.string');
        40goog.require('goog.userAgent');
        41
        42goog.forwardDeclare('goog.events.BrowserEvent');
        43goog.forwardDeclare('goog.events.Event');
        44
        45
        46/**
        47 * Sets a style value on an element.
        48 *
        49 * This function is not indended to patch issues in the browser's style
        50 * handling, but to allow easy programmatic access to setting dash-separated
        51 * style properties. An example is setting a batch of properties from a data
        52 * object without overwriting old styles. When possible, use native APIs:
        53 * elem.style.propertyKey = 'value' or (if obliterating old styles is fine)
        54 * elem.style.cssText = 'property1: value1; property2: value2'.
        55 *
        56 * @param {Element} element The element to change.
        57 * @param {string|Object} style If a string, a style name. If an object, a hash
        58 * of style names to style values.
        59 * @param {string|number|boolean=} opt_value If style was a string, then this
        60 * should be the value.
        61 */
        62goog.style.setStyle = function(element, style, opt_value) {
        63 if (goog.isString(style)) {
        64 goog.style.setStyle_(element, opt_value, style);
        65 } else {
        66 for (var key in style) {
        67 goog.style.setStyle_(element, style[key], key);
        68 }
        69 }
        70};
        71
        72
        73/**
        74 * Sets a style value on an element, with parameters swapped to work with
        75 * {@code goog.object.forEach()}. Prepends a vendor-specific prefix when
        76 * necessary.
        77 * @param {Element} element The element to change.
        78 * @param {string|number|boolean|undefined} value Style value.
        79 * @param {string} style Style name.
        80 * @private
        81 */
        82goog.style.setStyle_ = function(element, value, style) {
        83 var propertyName = goog.style.getVendorJsStyleName_(element, style);
        84
        85 if (propertyName) {
        86 element.style[propertyName] = value;
        87 }
        88};
        89
        90
        91/**
        92 * Style name cache that stores previous property name lookups.
        93 *
        94 * This is used by setStyle to speed up property lookups, entries look like:
        95 * { StyleName: ActualPropertyName }
        96 *
        97 * @private {!Object<string, string>}
        98 */
        99goog.style.styleNameCache_ = {};
        100
        101
        102/**
        103 * Returns the style property name in camel-case. If it does not exist and a
        104 * vendor-specific version of the property does exist, then return the vendor-
        105 * specific property name instead.
        106 * @param {Element} element The element to change.
        107 * @param {string} style Style name.
        108 * @return {string} Vendor-specific style.
        109 * @private
        110 */
        111goog.style.getVendorJsStyleName_ = function(element, style) {
        112 var propertyName = goog.style.styleNameCache_[style];
        113 if (!propertyName) {
        114 var camelStyle = goog.string.toCamelCase(style);
        115 propertyName = camelStyle;
        116
        117 if (element.style[camelStyle] === undefined) {
        118 var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
        119 goog.string.toTitleCase(camelStyle);
        120
        121 if (element.style[prefixedStyle] !== undefined) {
        122 propertyName = prefixedStyle;
        123 }
        124 }
        125 goog.style.styleNameCache_[style] = propertyName;
        126 }
        127
        128 return propertyName;
        129};
        130
        131
        132/**
        133 * Returns the style property name in CSS notation. If it does not exist and a
        134 * vendor-specific version of the property does exist, then return the vendor-
        135 * specific property name instead.
        136 * @param {Element} element The element to change.
        137 * @param {string} style Style name.
        138 * @return {string} Vendor-specific style.
        139 * @private
        140 */
        141goog.style.getVendorStyleName_ = function(element, style) {
        142 var camelStyle = goog.string.toCamelCase(style);
        143
        144 if (element.style[camelStyle] === undefined) {
        145 var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
        146 goog.string.toTitleCase(camelStyle);
        147
        148 if (element.style[prefixedStyle] !== undefined) {
        149 return goog.dom.vendor.getVendorPrefix() + '-' + style;
        150 }
        151 }
        152
        153 return style;
        154};
        155
        156
        157/**
        158 * Retrieves an explicitly-set style value of a node. This returns '' if there
        159 * isn't a style attribute on the element or if this style property has not been
        160 * explicitly set in script.
        161 *
        162 * @param {Element} element Element to get style of.
        163 * @param {string} property Property to get, css-style (if you have a camel-case
        164 * property, use element.style[style]).
        165 * @return {string} Style value.
        166 */
        167goog.style.getStyle = function(element, property) {
        168 // element.style is '' for well-known properties which are unset.
        169 // For for browser specific styles as 'filter' is undefined
        170 // so we need to return '' explicitly to make it consistent across
        171 // browsers.
        172 var styleValue = element.style[goog.string.toCamelCase(property)];
        173
        174 // Using typeof here because of a bug in Safari 5.1, where this value
        175 // was undefined, but === undefined returned false.
        176 if (typeof(styleValue) !== 'undefined') {
        177 return styleValue;
        178 }
        179
        180 return element.style[goog.style.getVendorJsStyleName_(element, property)] ||
        181 '';
        182};
        183
        184
        185/**
        186 * Retrieves a computed style value of a node. It returns empty string if the
        187 * value cannot be computed (which will be the case in Internet Explorer) or
        188 * "none" if the property requested is an SVG one and it has not been
        189 * explicitly set (firefox and webkit).
        190 *
        191 * @param {Element} element Element to get style of.
        192 * @param {string} property Property to get (camel-case).
        193 * @return {string} Style value.
        194 */
        195goog.style.getComputedStyle = function(element, property) {
        196 var doc = goog.dom.getOwnerDocument(element);
        197 if (doc.defaultView && doc.defaultView.getComputedStyle) {
        198 var styles = doc.defaultView.getComputedStyle(element, null);
        199 if (styles) {
        200 // element.style[..] is undefined for browser specific styles
        201 // as 'filter'.
        202 return styles[property] || styles.getPropertyValue(property) || '';
        203 }
        204 }
        205
        206 return '';
        207};
        208
        209
        210/**
        211 * Gets the cascaded style value of a node, or null if the value cannot be
        212 * computed (only Internet Explorer can do this).
        213 *
        214 * @param {Element} element Element to get style of.
        215 * @param {string} style Property to get (camel-case).
        216 * @return {string} Style value.
        217 */
        218goog.style.getCascadedStyle = function(element, style) {
        219 // TODO(nicksantos): This should be documented to return null. #fixTypes
        220 return element.currentStyle ? element.currentStyle[style] : null;
        221};
        222
        223
        224/**
        225 * Cross-browser pseudo get computed style. It returns the computed style where
        226 * available. If not available it tries the cascaded style value (IE
        227 * currentStyle) and in worst case the inline style value. It shouldn't be
        228 * called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for
        229 * discussion.
        230 *
        231 * @param {Element} element Element to get style of.
        232 * @param {string} style Property to get (must be camelCase, not css-style.).
        233 * @return {string} Style value.
        234 * @private
        235 */
        236goog.style.getStyle_ = function(element, style) {
        237 return goog.style.getComputedStyle(element, style) ||
        238 goog.style.getCascadedStyle(element, style) ||
        239 (element.style && element.style[style]);
        240};
        241
        242
        243/**
        244 * Retrieves the computed value of the box-sizing CSS attribute.
        245 * Browser support: http://caniuse.com/css3-boxsizing.
        246 * @param {!Element} element The element whose box-sizing to get.
        247 * @return {?string} 'content-box', 'border-box' or 'padding-box'. null if
        248 * box-sizing is not supported (IE7 and below).
        249 */
        250goog.style.getComputedBoxSizing = function(element) {
        251 return goog.style.getStyle_(element, 'boxSizing') ||
        252 goog.style.getStyle_(element, 'MozBoxSizing') ||
        253 goog.style.getStyle_(element, 'WebkitBoxSizing') || null;
        254};
        255
        256
        257/**
        258 * Retrieves the computed value of the position CSS attribute.
        259 * @param {Element} element The element to get the position of.
        260 * @return {string} Position value.
        261 */
        262goog.style.getComputedPosition = function(element) {
        263 return goog.style.getStyle_(element, 'position');
        264};
        265
        266
        267/**
        268 * Retrieves the computed background color string for a given element. The
        269 * string returned is suitable for assigning to another element's
        270 * background-color, but is not guaranteed to be in any particular string
        271 * format. Accessing the color in a numeric form may not be possible in all
        272 * browsers or with all input.
        273 *
        274 * If the background color for the element is defined as a hexadecimal value,
        275 * the resulting string can be parsed by goog.color.parse in all supported
        276 * browsers.
        277 *
        278 * Whether named colors like "red" or "lightblue" get translated into a
        279 * format which can be parsed is browser dependent. Calling this function on
        280 * transparent elements will return "transparent" in most browsers or
        281 * "rgba(0, 0, 0, 0)" in WebKit.
        282 * @param {Element} element The element to get the background color of.
        283 * @return {string} The computed string value of the background color.
        284 */
        285goog.style.getBackgroundColor = function(element) {
        286 return goog.style.getStyle_(element, 'backgroundColor');
        287};
        288
        289
        290/**
        291 * Retrieves the computed value of the overflow-x CSS attribute.
        292 * @param {Element} element The element to get the overflow-x of.
        293 * @return {string} The computed string value of the overflow-x attribute.
        294 */
        295goog.style.getComputedOverflowX = function(element) {
        296 return goog.style.getStyle_(element, 'overflowX');
        297};
        298
        299
        300/**
        301 * Retrieves the computed value of the overflow-y CSS attribute.
        302 * @param {Element} element The element to get the overflow-y of.
        303 * @return {string} The computed string value of the overflow-y attribute.
        304 */
        305goog.style.getComputedOverflowY = function(element) {
        306 return goog.style.getStyle_(element, 'overflowY');
        307};
        308
        309
        310/**
        311 * Retrieves the computed value of the z-index CSS attribute.
        312 * @param {Element} element The element to get the z-index of.
        313 * @return {string|number} The computed value of the z-index attribute.
        314 */
        315goog.style.getComputedZIndex = function(element) {
        316 return goog.style.getStyle_(element, 'zIndex');
        317};
        318
        319
        320/**
        321 * Retrieves the computed value of the text-align CSS attribute.
        322 * @param {Element} element The element to get the text-align of.
        323 * @return {string} The computed string value of the text-align attribute.
        324 */
        325goog.style.getComputedTextAlign = function(element) {
        326 return goog.style.getStyle_(element, 'textAlign');
        327};
        328
        329
        330/**
        331 * Retrieves the computed value of the cursor CSS attribute.
        332 * @param {Element} element The element to get the cursor of.
        333 * @return {string} The computed string value of the cursor attribute.
        334 */
        335goog.style.getComputedCursor = function(element) {
        336 return goog.style.getStyle_(element, 'cursor');
        337};
        338
        339
        340/**
        341 * Retrieves the computed value of the CSS transform attribute.
        342 * @param {Element} element The element to get the transform of.
        343 * @return {string} The computed string representation of the transform matrix.
        344 */
        345goog.style.getComputedTransform = function(element) {
        346 var property = goog.style.getVendorStyleName_(element, 'transform');
        347 return goog.style.getStyle_(element, property) ||
        348 goog.style.getStyle_(element, 'transform');
        349};
        350
        351
        352/**
        353 * Sets the top/left values of an element. If no unit is specified in the
        354 * argument then it will add px. The second argument is required if the first
        355 * argument is a string or number and is ignored if the first argument
        356 * is a coordinate.
        357 * @param {Element} el Element to move.
        358 * @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.
        359 * @param {string|number=} opt_arg2 Top position.
        360 */
        361goog.style.setPosition = function(el, arg1, opt_arg2) {
        362 var x, y;
        363
        364 if (arg1 instanceof goog.math.Coordinate) {
        365 x = arg1.x;
        366 y = arg1.y;
        367 } else {
        368 x = arg1;
        369 y = opt_arg2;
        370 }
        371
        372 el.style.left = goog.style.getPixelStyleValue_(
        373 /** @type {number|string} */ (x), false);
        374 el.style.top = goog.style.getPixelStyleValue_(
        375 /** @type {number|string} */ (y), false);
        376};
        377
        378
        379/**
        380 * Gets the offsetLeft and offsetTop properties of an element and returns them
        381 * in a Coordinate object
        382 * @param {Element} element Element.
        383 * @return {!goog.math.Coordinate} The position.
        384 */
        385goog.style.getPosition = function(element) {
        386 return new goog.math.Coordinate(
        387 /** @type {!HTMLElement} */ (element).offsetLeft,
        388 /** @type {!HTMLElement} */ (element).offsetTop);
        389};
        390
        391
        392/**
        393 * Returns the viewport element for a particular document
        394 * @param {Node=} opt_node DOM node (Document is OK) to get the viewport element
        395 * of.
        396 * @return {Element} document.documentElement or document.body.
        397 */
        398goog.style.getClientViewportElement = function(opt_node) {
        399 var doc;
        400 if (opt_node) {
        401 doc = goog.dom.getOwnerDocument(opt_node);
        402 } else {
        403 doc = goog.dom.getDocument();
        404 }
        405
        406 // In old IE versions the document.body represented the viewport
        407 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
        408 !goog.dom.getDomHelper(doc).isCss1CompatMode()) {
        409 return doc.body;
        410 }
        411 return doc.documentElement;
        412};
        413
        414
        415/**
        416 * Calculates the viewport coordinates relative to the page/document
        417 * containing the node. The viewport may be the browser viewport for
        418 * non-iframe document, or the iframe container for iframe'd document.
        419 * @param {!Document} doc The document to use as the reference point.
        420 * @return {!goog.math.Coordinate} The page offset of the viewport.
        421 */
        422goog.style.getViewportPageOffset = function(doc) {
        423 var body = doc.body;
        424 var documentElement = doc.documentElement;
        425 var scrollLeft = body.scrollLeft || documentElement.scrollLeft;
        426 var scrollTop = body.scrollTop || documentElement.scrollTop;
        427 return new goog.math.Coordinate(scrollLeft, scrollTop);
        428};
        429
        430
        431/**
        432 * Gets the client rectangle of the DOM element.
        433 *
        434 * getBoundingClientRect is part of a new CSS object model draft (with a
        435 * long-time presence in IE), replacing the error-prone parent offset
        436 * computation and the now-deprecated Gecko getBoxObjectFor.
        437 *
        438 * This utility patches common browser bugs in getBoundingClientRect. It
        439 * will fail if getBoundingClientRect is unsupported.
        440 *
        441 * If the element is not in the DOM, the result is undefined, and an error may
        442 * be thrown depending on user agent.
        443 *
        444 * @param {!Element} el The element whose bounding rectangle is being queried.
        445 * @return {Object} A native bounding rectangle with numerical left, top,
        446 * right, and bottom. Reported by Firefox to be of object type ClientRect.
        447 * @private
        448 */
        449goog.style.getBoundingClientRect_ = function(el) {
        450 var rect;
        451 try {
        452 rect = el.getBoundingClientRect();
        453 } catch (e) {
        454 // In IE < 9, calling getBoundingClientRect on an orphan element raises an
        455 // "Unspecified Error". All other browsers return zeros.
        456 return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};
        457 }
        458
        459 // Patch the result in IE only, so that this function can be inlined if
        460 // compiled for non-IE.
        461 if (goog.userAgent.IE && el.ownerDocument.body) {
        462
        463 // In IE, most of the time, 2 extra pixels are added to the top and left
        464 // due to the implicit 2-pixel inset border. In IE6/7 quirks mode and
        465 // IE6 standards mode, this border can be overridden by setting the
        466 // document element's border to zero -- thus, we cannot rely on the
        467 // offset always being 2 pixels.
        468
        469 // In quirks mode, the offset can be determined by querying the body's
        470 // clientLeft/clientTop, but in standards mode, it is found by querying
        471 // the document element's clientLeft/clientTop. Since we already called
        472 // getBoundingClientRect we have already forced a reflow, so it is not
        473 // too expensive just to query them all.
        474
        475 // See: http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx
        476 var doc = el.ownerDocument;
        477 rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft;
        478 rect.top -= doc.documentElement.clientTop + doc.body.clientTop;
        479 }
        480 return rect;
        481};
        482
        483
        484/**
        485 * Returns the first parent that could affect the position of a given element.
        486 * @param {Element} element The element to get the offset parent for.
        487 * @return {Element} The first offset parent or null if one cannot be found.
        488 */
        489goog.style.getOffsetParent = function(element) {
        490 // element.offsetParent does the right thing in IE7 and below. In other
        491 // browsers it only includes elements with position absolute, relative or
        492 // fixed, not elements with overflow set to auto or scroll.
        493 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {
        494 return element.offsetParent;
        495 }
        496
        497 var doc = goog.dom.getOwnerDocument(element);
        498 var positionStyle = goog.style.getStyle_(element, 'position');
        499 var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';
        500 for (var parent = element.parentNode; parent && parent != doc;
        501 parent = parent.parentNode) {
        502 // Skip shadowDOM roots.
        503 if (parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT &&
        504 parent.host) {
        505 parent = parent.host;
        506 }
        507 positionStyle =
        508 goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');
        509 skipStatic = skipStatic && positionStyle == 'static' &&
        510 parent != doc.documentElement && parent != doc.body;
        511 if (!skipStatic && (parent.scrollWidth > parent.clientWidth ||
        512 parent.scrollHeight > parent.clientHeight ||
        513 positionStyle == 'fixed' ||
        514 positionStyle == 'absolute' ||
        515 positionStyle == 'relative')) {
        516 return /** @type {!Element} */ (parent);
        517 }
        518 }
        519 return null;
        520};
        521
        522
        523/**
        524 * Calculates and returns the visible rectangle for a given element. Returns a
        525 * box describing the visible portion of the nearest scrollable offset ancestor.
        526 * Coordinates are given relative to the document.
        527 *
        528 * @param {Element} element Element to get the visible rect for.
        529 * @return {goog.math.Box} Bounding elementBox describing the visible rect or
        530 * null if scrollable ancestor isn't inside the visible viewport.
        531 */
        532goog.style.getVisibleRectForElement = function(element) {
        533 var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);
        534 var dom = goog.dom.getDomHelper(element);
        535 var body = dom.getDocument().body;
        536 var documentElement = dom.getDocument().documentElement;
        537 var scrollEl = dom.getDocumentScrollElement();
        538
        539 // Determine the size of the visible rect by climbing the dom accounting for
        540 // all scrollable containers.
        541 for (var el = element; el = goog.style.getOffsetParent(el); ) {
        542 // clientWidth is zero for inline block elements in IE.
        543 // on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0
        544 if ((!goog.userAgent.IE || el.clientWidth != 0) &&
        545 (!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&
        546 // body may have overflow set on it, yet we still get the entire
        547 // viewport. In some browsers, el.offsetParent may be
        548 // document.documentElement, so check for that too.
        549 (el != body && el != documentElement &&
        550 goog.style.getStyle_(el, 'overflow') != 'visible')) {
        551 var pos = goog.style.getPageOffset(el);
        552 var client = goog.style.getClientLeftTop(el);
        553 pos.x += client.x;
        554 pos.y += client.y;
        555
        556 visibleRect.top = Math.max(visibleRect.top, pos.y);
        557 visibleRect.right = Math.min(visibleRect.right,
        558 pos.x + el.clientWidth);
        559 visibleRect.bottom = Math.min(visibleRect.bottom,
        560 pos.y + el.clientHeight);
        561 visibleRect.left = Math.max(visibleRect.left, pos.x);
        562 }
        563 }
        564
        565 // Clip by window's viewport.
        566 var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;
        567 visibleRect.left = Math.max(visibleRect.left, scrollX);
        568 visibleRect.top = Math.max(visibleRect.top, scrollY);
        569 var winSize = dom.getViewportSize();
        570 visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);
        571 visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);
        572 return visibleRect.top >= 0 && visibleRect.left >= 0 &&
        573 visibleRect.bottom > visibleRect.top &&
        574 visibleRect.right > visibleRect.left ?
        575 visibleRect : null;
        576};
        577
        578
        579/**
        580 * Calculate the scroll position of {@code container} with the minimum amount so
        581 * that the content and the borders of the given {@code element} become visible.
        582 * If the element is bigger than the container, its top left corner will be
        583 * aligned as close to the container's top left corner as possible.
        584 *
        585 * @param {Element} element The element to make visible.
        586 * @param {Element=} opt_container The container to scroll. If not set, then the
        587 * document scroll element will be used.
        588 * @param {boolean=} opt_center Whether to center the element in the container.
        589 * Defaults to false.
        590 * @return {!goog.math.Coordinate} The new scroll position of the container,
        591 * in form of goog.math.Coordinate(scrollLeft, scrollTop).
        592 */
        593goog.style.getContainerOffsetToScrollInto =
        594 function(element, opt_container, opt_center) {
        595 var container = opt_container || goog.dom.getDocumentScrollElement();
        596 // Absolute position of the element's border's top left corner.
        597 var elementPos = goog.style.getPageOffset(element);
        598 // Absolute position of the container's border's top left corner.
        599 var containerPos = goog.style.getPageOffset(container);
        600 var containerBorder = goog.style.getBorderBox(container);
        601 if (container == goog.dom.getDocumentScrollElement()) {
        602 // The element position is calculated based on the page offset, and the
        603 // document scroll element holds the scroll position within the page. We can
        604 // use the scroll position to calculate the relative position from the
        605 // element.
        606 var relX = elementPos.x - container.scrollLeft;
        607 var relY = elementPos.y - container.scrollTop;
        608 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
        609 // In older versions of IE getPageOffset(element) does not include the
        610 // container border so it has to be added to accomodate.
        611 relX += containerBorder.left;
        612 relY += containerBorder.top;
        613 }
        614 } else {
        615 // Relative pos. of the element's border box to the container's content box.
        616 var relX = elementPos.x - containerPos.x - containerBorder.left;
        617 var relY = elementPos.y - containerPos.y - containerBorder.top;
        618 }
        619 // How much the element can move in the container, i.e. the difference between
        620 // the element's bottom-right-most and top-left-most position where it's
        621 // fully visible.
        622 var spaceX = container.clientWidth -
        623 /** @type {HTMLElement} */ (element).offsetWidth;
        624 var spaceY = container.clientHeight -
        625 /** @type {HTMLElement} */ (element).offsetHeight;
        626
        627 var scrollLeft = container.scrollLeft;
        628 var scrollTop = container.scrollTop;
        629 if (opt_center) {
        630 // All browsers round non-integer scroll positions down.
        631 scrollLeft += relX - spaceX / 2;
        632 scrollTop += relY - spaceY / 2;
        633 } else {
        634 // This formula was designed to give the correct scroll values in the
        635 // following cases:
        636 // - element is higher than container (spaceY < 0) => scroll down by relY
        637 // - element is not higher that container (spaceY >= 0):
        638 // - it is above container (relY < 0) => scroll up by abs(relY)
        639 // - it is below container (relY > spaceY) => scroll down by relY - spaceY
        640 // - it is in the container => don't scroll
        641 scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));
        642 scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));
        643 }
        644 return new goog.math.Coordinate(scrollLeft, scrollTop);
        645};
        646
        647
        648/**
        649 * Changes the scroll position of {@code container} with the minimum amount so
        650 * that the content and the borders of the given {@code element} become visible.
        651 * If the element is bigger than the container, its top left corner will be
        652 * aligned as close to the container's top left corner as possible.
        653 *
        654 * @param {Element} element The element to make visible.
        655 * @param {Element=} opt_container The container to scroll. If not set, then the
        656 * document scroll element will be used.
        657 * @param {boolean=} opt_center Whether to center the element in the container.
        658 * Defaults to false.
        659 */
        660goog.style.scrollIntoContainerView =
        661 function(element, opt_container, opt_center) {
        662 var container = opt_container || goog.dom.getDocumentScrollElement();
        663 var offset =
        664 goog.style.getContainerOffsetToScrollInto(element, container, opt_center);
        665 container.scrollLeft = offset.x;
        666 container.scrollTop = offset.y;
        667};
        668
        669
        670/**
        671 * Returns clientLeft (width of the left border and, if the directionality is
        672 * right to left, the vertical scrollbar) and clientTop as a coordinate object.
        673 *
        674 * @param {Element} el Element to get clientLeft for.
        675 * @return {!goog.math.Coordinate} Client left and top.
        676 */
        677goog.style.getClientLeftTop = function(el) {
        678 return new goog.math.Coordinate(el.clientLeft, el.clientTop);
        679};
        680
        681
        682/**
        683 * Returns a Coordinate object relative to the top-left of the HTML document.
        684 * Implemented as a single function to save having to do two recursive loops in
        685 * opera and safari just to get both coordinates. If you just want one value do
        686 * use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but
        687 * note if you call both those methods the tree will be analysed twice.
        688 *
        689 * @param {Element} el Element to get the page offset for.
        690 * @return {!goog.math.Coordinate} The page offset.
        691 */
        692goog.style.getPageOffset = function(el) {
        693 var doc = goog.dom.getOwnerDocument(el);
        694 // TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.
        695 goog.asserts.assertObject(el, 'Parameter is required');
        696
        697 // NOTE(arv): If element is hidden (display none or disconnected or any the
        698 // ancestors are hidden) we get (0,0) by default but we still do the
        699 // accumulation of scroll position.
        700
        701 // TODO(arv): Should we check if the node is disconnected and in that case
        702 // return (0,0)?
        703
        704 var pos = new goog.math.Coordinate(0, 0);
        705 var viewportElement = goog.style.getClientViewportElement(doc);
        706 if (el == viewportElement) {
        707 // viewport is always at 0,0 as that defined the coordinate system for this
        708 // function - this avoids special case checks in the code below
        709 return pos;
        710 }
        711
        712 var box = goog.style.getBoundingClientRect_(el);
        713 // Must add the scroll coordinates in to get the absolute page offset
        714 // of element since getBoundingClientRect returns relative coordinates to
        715 // the viewport.
        716 var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();
        717 pos.x = box.left + scrollCoord.x;
        718 pos.y = box.top + scrollCoord.y;
        719
        720 return pos;
        721};
        722
        723
        724/**
        725 * Returns the left coordinate of an element relative to the HTML document
        726 * @param {Element} el Elements.
        727 * @return {number} The left coordinate.
        728 */
        729goog.style.getPageOffsetLeft = function(el) {
        730 return goog.style.getPageOffset(el).x;
        731};
        732
        733
        734/**
        735 * Returns the top coordinate of an element relative to the HTML document
        736 * @param {Element} el Elements.
        737 * @return {number} The top coordinate.
        738 */
        739goog.style.getPageOffsetTop = function(el) {
        740 return goog.style.getPageOffset(el).y;
        741};
        742
        743
        744/**
        745 * Returns a Coordinate object relative to the top-left of an HTML document
        746 * in an ancestor frame of this element. Used for measuring the position of
        747 * an element inside a frame relative to a containing frame.
        748 *
        749 * @param {Element} el Element to get the page offset for.
        750 * @param {Window} relativeWin The window to measure relative to. If relativeWin
        751 * is not in the ancestor frame chain of the element, we measure relative to
        752 * the top-most window.
        753 * @return {!goog.math.Coordinate} The page offset.
        754 */
        755goog.style.getFramedPageOffset = function(el, relativeWin) {
        756 var position = new goog.math.Coordinate(0, 0);
        757
        758 // Iterate up the ancestor frame chain, keeping track of the current window
        759 // and the current element in that window.
        760 var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));
        761 var currentEl = el;
        762 do {
        763 // if we're at the top window, we want to get the page offset.
        764 // if we're at an inner frame, we only want to get the window position
        765 // so that we can determine the actual page offset in the context of
        766 // the outer window.
        767 var offset = currentWin == relativeWin ?
        768 goog.style.getPageOffset(currentEl) :
        769 goog.style.getClientPositionForElement_(
        770 goog.asserts.assert(currentEl));
        771
        772 position.x += offset.x;
        773 position.y += offset.y;
        774 } while (currentWin && currentWin != relativeWin &&
        775 currentWin != currentWin.parent &&
        776 (currentEl = currentWin.frameElement) &&
        777 (currentWin = currentWin.parent));
        778
        779 return position;
        780};
        781
        782
        783/**
        784 * Translates the specified rect relative to origBase page, for newBase page.
        785 * If origBase and newBase are the same, this function does nothing.
        786 *
        787 * @param {goog.math.Rect} rect The source rectangle relative to origBase page,
        788 * and it will have the translated result.
        789 * @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.
        790 * @param {goog.dom.DomHelper} newBase The DomHelper for the resultant
        791 * coordinate. This must be a DOM for an ancestor frame of origBase
        792 * or the same as origBase.
        793 */
        794goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {
        795 if (origBase.getDocument() != newBase.getDocument()) {
        796 var body = origBase.getDocument().body;
        797 var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());
        798
        799 // Adjust Body's margin.
        800 pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));
        801
        802 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
        803 !origBase.isCss1CompatMode()) {
        804 pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());
        805 }
        806
        807 rect.left += pos.x;
        808 rect.top += pos.y;
        809 }
        810};
        811
        812
        813/**
        814 * Returns the position of an element relative to another element in the
        815 * document. A relative to B
        816 * @param {Element|Event|goog.events.Event} a Element or mouse event whose
        817 * position we're calculating.
        818 * @param {Element|Event|goog.events.Event} b Element or mouse event position
        819 * is relative to.
        820 * @return {!goog.math.Coordinate} The relative position.
        821 */
        822goog.style.getRelativePosition = function(a, b) {
        823 var ap = goog.style.getClientPosition(a);
        824 var bp = goog.style.getClientPosition(b);
        825 return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);
        826};
        827
        828
        829/**
        830 * Returns the position of the event or the element's border box relative to
        831 * the client viewport.
        832 * @param {!Element} el Element whose position to get.
        833 * @return {!goog.math.Coordinate} The position.
        834 * @private
        835 */
        836goog.style.getClientPositionForElement_ = function(el) {
        837 var box = goog.style.getBoundingClientRect_(el);
        838 return new goog.math.Coordinate(box.left, box.top);
        839};
        840
        841
        842/**
        843 * Returns the position of the event or the element's border box relative to
        844 * the client viewport. If an event is passed, and if this event is a "touch"
        845 * event, then the position of the first changedTouches will be returned.
        846 * @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.
        847 * @return {!goog.math.Coordinate} The position.
        848 */
        849goog.style.getClientPosition = function(el) {
        850 goog.asserts.assert(el);
        851 if (el.nodeType == goog.dom.NodeType.ELEMENT) {
        852 return goog.style.getClientPositionForElement_(
        853 /** @type {!Element} */ (el));
        854 } else {
        855 var targetEvent = el.changedTouches ? el.changedTouches[0] : el;
        856 return new goog.math.Coordinate(
        857 targetEvent.clientX,
        858 targetEvent.clientY);
        859 }
        860};
        861
        862
        863/**
        864 * Moves an element to the given coordinates relative to the client viewport.
        865 * @param {Element} el Absolutely positioned element to set page offset for.
        866 * It must be in the document.
        867 * @param {number|goog.math.Coordinate} x Left position of the element's margin
        868 * box or a coordinate object.
        869 * @param {number=} opt_y Top position of the element's margin box.
        870 */
        871goog.style.setPageOffset = function(el, x, opt_y) {
        872 // Get current pageoffset
        873 var cur = goog.style.getPageOffset(el);
        874
        875 if (x instanceof goog.math.Coordinate) {
        876 opt_y = x.y;
        877 x = x.x;
        878 }
        879
        880 // NOTE(arv): We cannot allow strings for x and y. We could but that would
        881 // require us to manually transform between different units
        882
        883 // Work out deltas
        884 var dx = x - cur.x;
        885 var dy = opt_y - cur.y;
        886
        887 // Set position to current left/top + delta
        888 goog.style.setPosition(el, /** @type {!HTMLElement} */ (el).offsetLeft + dx,
        889 /** @type {!HTMLElement} */ (el).offsetTop + dy);
        890};
        891
        892
        893/**
        894 * Sets the width/height values of an element. If an argument is numeric,
        895 * or a goog.math.Size is passed, it is assumed to be pixels and will add
        896 * 'px' after converting it to an integer in string form. (This just sets the
        897 * CSS width and height properties so it might set content-box or border-box
        898 * size depending on the box model the browser is using.)
        899 *
        900 * @param {Element} element Element to set the size of.
        901 * @param {string|number|goog.math.Size} w Width of the element, or a
        902 * size object.
        903 * @param {string|number=} opt_h Height of the element. Required if w is not a
        904 * size object.
        905 */
        906goog.style.setSize = function(element, w, opt_h) {
        907 var h;
        908 if (w instanceof goog.math.Size) {
        909 h = w.height;
        910 w = w.width;
        911 } else {
        912 if (opt_h == undefined) {
        913 throw Error('missing height argument');
        914 }
        915 h = opt_h;
        916 }
        917
        918 goog.style.setWidth(element, /** @type {string|number} */ (w));
        919 goog.style.setHeight(element, h);
        920};
        921
        922
        923/**
        924 * Helper function to create a string to be set into a pixel-value style
        925 * property of an element. Can round to the nearest integer value.
        926 *
        927 * @param {string|number} value The style value to be used. If a number,
        928 * 'px' will be appended, otherwise the value will be applied directly.
        929 * @param {boolean} round Whether to round the nearest integer (if property
        930 * is a number).
        931 * @return {string} The string value for the property.
        932 * @private
        933 */
        934goog.style.getPixelStyleValue_ = function(value, round) {
        935 if (typeof value == 'number') {
        936 value = (round ? Math.round(value) : value) + 'px';
        937 }
        938
        939 return value;
        940};
        941
        942
        943/**
        944 * Set the height of an element. Sets the element's style property.
        945 * @param {Element} element Element to set the height of.
        946 * @param {string|number} height The height value to set. If a number, 'px'
        947 * will be appended, otherwise the value will be applied directly.
        948 */
        949goog.style.setHeight = function(element, height) {
        950 element.style.height = goog.style.getPixelStyleValue_(height, true);
        951};
        952
        953
        954/**
        955 * Set the width of an element. Sets the element's style property.
        956 * @param {Element} element Element to set the width of.
        957 * @param {string|number} width The width value to set. If a number, 'px'
        958 * will be appended, otherwise the value will be applied directly.
        959 */
        960goog.style.setWidth = function(element, width) {
        961 element.style.width = goog.style.getPixelStyleValue_(width, true);
        962};
        963
        964
        965/**
        966 * Gets the height and width of an element, even if its display is none.
        967 *
        968 * Specifically, this returns the height and width of the border box,
        969 * irrespective of the box model in effect.
        970 *
        971 * Note that this function does not take CSS transforms into account. Please see
        972 * {@code goog.style.getTransformedSize}.
        973 * @param {Element} element Element to get size of.
        974 * @return {!goog.math.Size} Object with width/height properties.
        975 */
        976goog.style.getSize = function(element) {
        977 return goog.style.evaluateWithTemporaryDisplay_(
        978 goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));
        979};
        980
        981
        982/**
        983 * Call {@code fn} on {@code element} such that {@code element}'s dimensions are
        984 * accurate when it's passed to {@code fn}.
        985 * @param {function(!Element): T} fn Function to call with {@code element} as
        986 * an argument after temporarily changing {@code element}'s display such
        987 * that its dimensions are accurate.
        988 * @param {!Element} element Element (which may have display none) to use as
        989 * argument to {@code fn}.
        990 * @return {T} Value returned by calling {@code fn} with {@code element}.
        991 * @template T
        992 * @private
        993 */
        994goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {
        995 if (goog.style.getStyle_(element, 'display') != 'none') {
        996 return fn(element);
        997 }
        998
        999 var style = element.style;
        1000 var originalDisplay = style.display;
        1001 var originalVisibility = style.visibility;
        1002 var originalPosition = style.position;
        1003
        1004 style.visibility = 'hidden';
        1005 style.position = 'absolute';
        1006 style.display = 'inline';
        1007
        1008 var retVal = fn(element);
        1009
        1010 style.display = originalDisplay;
        1011 style.position = originalPosition;
        1012 style.visibility = originalVisibility;
        1013
        1014 return retVal;
        1015};
        1016
        1017
        1018/**
        1019 * Gets the height and width of an element when the display is not none.
        1020 * @param {Element} element Element to get size of.
        1021 * @return {!goog.math.Size} Object with width/height properties.
        1022 * @private
        1023 */
        1024goog.style.getSizeWithDisplay_ = function(element) {
        1025 var offsetWidth = /** @type {!HTMLElement} */ (element).offsetWidth;
        1026 var offsetHeight = /** @type {!HTMLElement} */ (element).offsetHeight;
        1027 var webkitOffsetsZero =
        1028 goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;
        1029 if ((!goog.isDef(offsetWidth) || webkitOffsetsZero) &&
        1030 element.getBoundingClientRect) {
        1031 // Fall back to calling getBoundingClientRect when offsetWidth or
        1032 // offsetHeight are not defined, or when they are zero in WebKit browsers.
        1033 // This makes sure that we return for the correct size for SVG elements, but
        1034 // will still return 0 on Webkit prior to 534.8, see
        1035 // http://trac.webkit.org/changeset/67252.
        1036 var clientRect = goog.style.getBoundingClientRect_(element);
        1037 return new goog.math.Size(clientRect.right - clientRect.left,
        1038 clientRect.bottom - clientRect.top);
        1039 }
        1040 return new goog.math.Size(offsetWidth, offsetHeight);
        1041};
        1042
        1043
        1044/**
        1045 * Gets the height and width of an element, post transform, even if its display
        1046 * is none.
        1047 *
        1048 * This is like {@code goog.style.getSize}, except:
        1049 * <ol>
        1050 * <li>Takes webkitTransforms such as rotate and scale into account.
        1051 * <li>Will return null if {@code element} doesn't respond to
        1052 * {@code getBoundingClientRect}.
        1053 * <li>Currently doesn't make sense on non-WebKit browsers which don't support
        1054 * webkitTransforms.
        1055 * </ol>
        1056 * @param {!Element} element Element to get size of.
        1057 * @return {goog.math.Size} Object with width/height properties.
        1058 */
        1059goog.style.getTransformedSize = function(element) {
        1060 if (!element.getBoundingClientRect) {
        1061 return null;
        1062 }
        1063
        1064 var clientRect = goog.style.evaluateWithTemporaryDisplay_(
        1065 goog.style.getBoundingClientRect_, element);
        1066 return new goog.math.Size(clientRect.right - clientRect.left,
        1067 clientRect.bottom - clientRect.top);
        1068};
        1069
        1070
        1071/**
        1072 * Returns a bounding rectangle for a given element in page space.
        1073 * @param {Element} element Element to get bounds of. Must not be display none.
        1074 * @return {!goog.math.Rect} Bounding rectangle for the element.
        1075 */
        1076goog.style.getBounds = function(element) {
        1077 var o = goog.style.getPageOffset(element);
        1078 var s = goog.style.getSize(element);
        1079 return new goog.math.Rect(o.x, o.y, s.width, s.height);
        1080};
        1081
        1082
        1083/**
        1084 * Converts a CSS selector in the form style-property to styleProperty.
        1085 * @param {*} selector CSS Selector.
        1086 * @return {string} Camel case selector.
        1087 * @deprecated Use goog.string.toCamelCase instead.
        1088 */
        1089goog.style.toCamelCase = function(selector) {
        1090 return goog.string.toCamelCase(String(selector));
        1091};
        1092
        1093
        1094/**
        1095 * Converts a CSS selector in the form styleProperty to style-property.
        1096 * @param {string} selector Camel case selector.
        1097 * @return {string} Selector cased.
        1098 * @deprecated Use goog.string.toSelectorCase instead.
        1099 */
        1100goog.style.toSelectorCase = function(selector) {
        1101 return goog.string.toSelectorCase(selector);
        1102};
        1103
        1104
        1105/**
        1106 * Gets the opacity of a node (x-browser). This gets the inline style opacity
        1107 * of the node, and does not take into account the cascaded or the computed
        1108 * style for this node.
        1109 * @param {Element} el Element whose opacity has to be found.
        1110 * @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}
        1111 * if the opacity is not set.
        1112 */
        1113goog.style.getOpacity = function(el) {
        1114 var style = el.style;
        1115 var result = '';
        1116 if ('opacity' in style) {
        1117 result = style.opacity;
        1118 } else if ('MozOpacity' in style) {
        1119 result = style.MozOpacity;
        1120 } else if ('filter' in style) {
        1121 var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);
        1122 if (match) {
        1123 result = String(match[1] / 100);
        1124 }
        1125 }
        1126 return result == '' ? result : Number(result);
        1127};
        1128
        1129
        1130/**
        1131 * Sets the opacity of a node (x-browser).
        1132 * @param {Element} el Elements whose opacity has to be set.
        1133 * @param {number|string} alpha Opacity between 0 and 1 or an empty string
        1134 * {@code ''} to clear the opacity.
        1135 */
        1136goog.style.setOpacity = function(el, alpha) {
        1137 var style = el.style;
        1138 if ('opacity' in style) {
        1139 style.opacity = alpha;
        1140 } else if ('MozOpacity' in style) {
        1141 style.MozOpacity = alpha;
        1142 } else if ('filter' in style) {
        1143 // TODO(arv): Overwriting the filter might have undesired side effects.
        1144 if (alpha === '') {
        1145 style.filter = '';
        1146 } else {
        1147 style.filter = 'alpha(opacity=' + alpha * 100 + ')';
        1148 }
        1149 }
        1150};
        1151
        1152
        1153/**
        1154 * Sets the background of an element to a transparent image in a browser-
        1155 * independent manner.
        1156 *
        1157 * This function does not support repeating backgrounds or alternate background
        1158 * positions to match the behavior of Internet Explorer. It also does not
        1159 * support sizingMethods other than crop since they cannot be replicated in
        1160 * browsers other than Internet Explorer.
        1161 *
        1162 * @param {Element} el The element to set background on.
        1163 * @param {string} src The image source URL.
        1164 */
        1165goog.style.setTransparentBackgroundImage = function(el, src) {
        1166 var style = el.style;
        1167 // It is safe to use the style.filter in IE only. In Safari 'filter' is in
        1168 // style object but access to style.filter causes it to throw an exception.
        1169 // Note: IE8 supports images with an alpha channel.
        1170 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
        1171 // See TODO in setOpacity.
        1172 style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
        1173 'src="' + src + '", sizingMethod="crop")';
        1174 } else {
        1175 // Set style properties individually instead of using background shorthand
        1176 // to prevent overwriting a pre-existing background color.
        1177 style.backgroundImage = 'url(' + src + ')';
        1178 style.backgroundPosition = 'top left';
        1179 style.backgroundRepeat = 'no-repeat';
        1180 }
        1181};
        1182
        1183
        1184/**
        1185 * Clears the background image of an element in a browser independent manner.
        1186 * @param {Element} el The element to clear background image for.
        1187 */
        1188goog.style.clearTransparentBackgroundImage = function(el) {
        1189 var style = el.style;
        1190 if ('filter' in style) {
        1191 // See TODO in setOpacity.
        1192 style.filter = '';
        1193 } else {
        1194 // Set style properties individually instead of using background shorthand
        1195 // to prevent overwriting a pre-existing background color.
        1196 style.backgroundImage = 'none';
        1197 }
        1198};
        1199
        1200
        1201/**
        1202 * Shows or hides an element from the page. Hiding the element is done by
        1203 * setting the display property to "none", removing the element from the
        1204 * rendering hierarchy so it takes up no space. To show the element, the default
        1205 * inherited display property is restored (defined either in stylesheets or by
        1206 * the browser's default style rules.)
        1207 *
        1208 * Caveat 1: if the inherited display property for the element is set to "none"
        1209 * by the stylesheets, that is the property that will be restored by a call to
        1210 * showElement(), effectively toggling the display between "none" and "none".
        1211 *
        1212 * Caveat 2: if the element display style is set inline (by setting either
        1213 * element.style.display or a style attribute in the HTML), a call to
        1214 * showElement will clear that setting and defer to the inherited style in the
        1215 * stylesheet.
        1216 * @param {Element} el Element to show or hide.
        1217 * @param {*} display True to render the element in its default style,
        1218 * false to disable rendering the element.
        1219 * @deprecated Use goog.style.setElementShown instead.
        1220 */
        1221goog.style.showElement = function(el, display) {
        1222 goog.style.setElementShown(el, display);
        1223};
        1224
        1225
        1226/**
        1227 * Shows or hides an element from the page. Hiding the element is done by
        1228 * setting the display property to "none", removing the element from the
        1229 * rendering hierarchy so it takes up no space. To show the element, the default
        1230 * inherited display property is restored (defined either in stylesheets or by
        1231 * the browser's default style rules).
        1232 *
        1233 * Caveat 1: if the inherited display property for the element is set to "none"
        1234 * by the stylesheets, that is the property that will be restored by a call to
        1235 * setElementShown(), effectively toggling the display between "none" and
        1236 * "none".
        1237 *
        1238 * Caveat 2: if the element display style is set inline (by setting either
        1239 * element.style.display or a style attribute in the HTML), a call to
        1240 * setElementShown will clear that setting and defer to the inherited style in
        1241 * the stylesheet.
        1242 * @param {Element} el Element to show or hide.
        1243 * @param {*} isShown True to render the element in its default style,
        1244 * false to disable rendering the element.
        1245 */
        1246goog.style.setElementShown = function(el, isShown) {
        1247 el.style.display = isShown ? '' : 'none';
        1248};
        1249
        1250
        1251/**
        1252 * Test whether the given element has been shown or hidden via a call to
        1253 * {@link #setElementShown}.
        1254 *
        1255 * Note this is strictly a companion method for a call
        1256 * to {@link #setElementShown} and the same caveats apply; in particular, this
        1257 * method does not guarantee that the return value will be consistent with
        1258 * whether or not the element is actually visible.
        1259 *
        1260 * @param {Element} el The element to test.
        1261 * @return {boolean} Whether the element has been shown.
        1262 * @see #setElementShown
        1263 */
        1264goog.style.isElementShown = function(el) {
        1265 return el.style.display != 'none';
        1266};
        1267
        1268
        1269/**
        1270 * Installs the styles string into the window that contains opt_element. If
        1271 * opt_element is null, the main window is used.
        1272 * @param {string} stylesString The style string to install.
        1273 * @param {Node=} opt_node Node whose parent document should have the
        1274 * styles installed.
        1275 * @return {Element|StyleSheet} The style element created.
        1276 */
        1277goog.style.installStyles = function(stylesString, opt_node) {
        1278 var dh = goog.dom.getDomHelper(opt_node);
        1279 var styleSheet = null;
        1280
        1281 // IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be
        1282 // undefined as of IE 11.
        1283 var doc = dh.getDocument();
        1284 if (goog.userAgent.IE && doc.createStyleSheet) {
        1285 styleSheet = doc.createStyleSheet();
        1286 goog.style.setStyles(styleSheet, stylesString);
        1287 } else {
        1288 var head = dh.getElementsByTagNameAndClass(goog.dom.TagName.HEAD)[0];
        1289
        1290 // In opera documents are not guaranteed to have a head element, thus we
        1291 // have to make sure one exists before using it.
        1292 if (!head) {
        1293 var body = dh.getElementsByTagNameAndClass(goog.dom.TagName.BODY)[0];
        1294 head = dh.createDom(goog.dom.TagName.HEAD);
        1295 body.parentNode.insertBefore(head, body);
        1296 }
        1297 styleSheet = dh.createDom(goog.dom.TagName.STYLE);
        1298 // NOTE(user): Setting styles after the style element has been appended
        1299 // to the head results in a nasty Webkit bug in certain scenarios. Please
        1300 // refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional
        1301 // details.
        1302 goog.style.setStyles(styleSheet, stylesString);
        1303 dh.appendChild(head, styleSheet);
        1304 }
        1305 return styleSheet;
        1306};
        1307
        1308
        1309/**
        1310 * Removes the styles added by {@link #installStyles}.
        1311 * @param {Element|StyleSheet} styleSheet The value returned by
        1312 * {@link #installStyles}.
        1313 */
        1314goog.style.uninstallStyles = function(styleSheet) {
        1315 var node = styleSheet.ownerNode || styleSheet.owningElement ||
        1316 /** @type {Element} */ (styleSheet);
        1317 goog.dom.removeNode(node);
        1318};
        1319
        1320
        1321/**
        1322 * Sets the content of a style element. The style element can be any valid
        1323 * style element. This element will have its content completely replaced by
        1324 * the new stylesString.
        1325 * @param {Element|StyleSheet} element A stylesheet element as returned by
        1326 * installStyles.
        1327 * @param {string} stylesString The new content of the stylesheet.
        1328 */
        1329goog.style.setStyles = function(element, stylesString) {
        1330 if (goog.userAgent.IE && goog.isDef(element.cssText)) {
        1331 // Adding the selectors individually caused the browser to hang if the
        1332 // selector was invalid or there were CSS comments. Setting the cssText of
        1333 // the style node works fine and ignores CSS that IE doesn't understand.
        1334 // However IE >= 11 doesn't support cssText any more, so we make sure that
        1335 // cssText is a defined property and otherwise fall back to innerHTML.
        1336 element.cssText = stylesString;
        1337 } else {
        1338 element.innerHTML = stylesString;
        1339 }
        1340};
        1341
        1342
        1343/**
        1344 * Sets 'white-space: pre-wrap' for a node (x-browser).
        1345 *
        1346 * There are as many ways of specifying pre-wrap as there are browsers.
        1347 *
        1348 * CSS3/IE8: white-space: pre-wrap;
        1349 * Mozilla: white-space: -moz-pre-wrap;
        1350 * Opera: white-space: -o-pre-wrap;
        1351 * IE6/7: white-space: pre; word-wrap: break-word;
        1352 *
        1353 * @param {Element} el Element to enable pre-wrap for.
        1354 */
        1355goog.style.setPreWrap = function(el) {
        1356 var style = el.style;
        1357 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
        1358 style.whiteSpace = 'pre';
        1359 style.wordWrap = 'break-word';
        1360 } else if (goog.userAgent.GECKO) {
        1361 style.whiteSpace = '-moz-pre-wrap';
        1362 } else {
        1363 style.whiteSpace = 'pre-wrap';
        1364 }
        1365};
        1366
        1367
        1368/**
        1369 * Sets 'display: inline-block' for an element (cross-browser).
        1370 * @param {Element} el Element to which the inline-block display style is to be
        1371 * applied.
        1372 * @see ../demos/inline_block_quirks.html
        1373 * @see ../demos/inline_block_standards.html
        1374 */
        1375goog.style.setInlineBlock = function(el) {
        1376 var style = el.style;
        1377 // Without position:relative, weirdness ensues. Just accept it and move on.
        1378 style.position = 'relative';
        1379
        1380 if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
        1381 // IE8 supports inline-block so fall through to the else
        1382 // Zoom:1 forces hasLayout, display:inline gives inline behavior.
        1383 style.zoom = '1';
        1384 style.display = 'inline';
        1385 } else {
        1386 // Opera, Webkit, and Safari seem to do OK with the standard inline-block
        1387 // style.
        1388 style.display = 'inline-block';
        1389 }
        1390};
        1391
        1392
        1393/**
        1394 * Returns true if the element is using right to left (rtl) direction.
        1395 * @param {Element} el The element to test.
        1396 * @return {boolean} True for right to left, false for left to right.
        1397 */
        1398goog.style.isRightToLeft = function(el) {
        1399 return 'rtl' == goog.style.getStyle_(el, 'direction');
        1400};
        1401
        1402
        1403/**
        1404 * The CSS style property corresponding to an element being
        1405 * unselectable on the current browser platform (null if none).
        1406 * Opera and IE instead use a DOM attribute 'unselectable'.
        1407 * @type {?string}
        1408 * @private
        1409 */
        1410goog.style.unselectableStyle_ =
        1411 goog.userAgent.GECKO ? 'MozUserSelect' :
        1412 goog.userAgent.WEBKIT ? 'WebkitUserSelect' :
        1413 null;
        1414
        1415
        1416/**
        1417 * Returns true if the element is set to be unselectable, false otherwise.
        1418 * Note that on some platforms (e.g. Mozilla), even if an element isn't set
        1419 * to be unselectable, it will behave as such if any of its ancestors is
        1420 * unselectable.
        1421 * @param {Element} el Element to check.
        1422 * @return {boolean} Whether the element is set to be unselectable.
        1423 */
        1424goog.style.isUnselectable = function(el) {
        1425 if (goog.style.unselectableStyle_) {
        1426 return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';
        1427 } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
        1428 return el.getAttribute('unselectable') == 'on';
        1429 }
        1430 return false;
        1431};
        1432
        1433
        1434/**
        1435 * Makes the element and its descendants selectable or unselectable. Note
        1436 * that on some platforms (e.g. Mozilla), even if an element isn't set to
        1437 * be unselectable, it will behave as such if any of its ancestors is
        1438 * unselectable.
        1439 * @param {Element} el The element to alter.
        1440 * @param {boolean} unselectable Whether the element and its descendants
        1441 * should be made unselectable.
        1442 * @param {boolean=} opt_noRecurse Whether to only alter the element's own
        1443 * selectable state, and leave its descendants alone; defaults to false.
        1444 */
        1445goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {
        1446 // TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?
        1447 var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;
        1448 var name = goog.style.unselectableStyle_;
        1449 if (name) {
        1450 // Add/remove the appropriate CSS style to/from the element and its
        1451 // descendants.
        1452 var value = unselectable ? 'none' : '';
        1453 // MathML elements do not have a style property. Verify before setting.
        1454 if (el.style) {
        1455 el.style[name] = value;
        1456 }
        1457 if (descendants) {
        1458 for (var i = 0, descendant; descendant = descendants[i]; i++) {
        1459 if (descendant.style) {
        1460 descendant.style[name] = value;
        1461 }
        1462 }
        1463 }
        1464 } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
        1465 // Toggle the 'unselectable' attribute on the element and its descendants.
        1466 var value = unselectable ? 'on' : '';
        1467 el.setAttribute('unselectable', value);
        1468 if (descendants) {
        1469 for (var i = 0, descendant; descendant = descendants[i]; i++) {
        1470 descendant.setAttribute('unselectable', value);
        1471 }
        1472 }
        1473 }
        1474};
        1475
        1476
        1477/**
        1478 * Gets the border box size for an element.
        1479 * @param {Element} element The element to get the size for.
        1480 * @return {!goog.math.Size} The border box size.
        1481 */
        1482goog.style.getBorderBoxSize = function(element) {
        1483 return new goog.math.Size(
        1484 /** @type {!HTMLElement} */ (element).offsetWidth,
        1485 /** @type {!HTMLElement} */ (element).offsetHeight);
        1486};
        1487
        1488
        1489/**
        1490 * Sets the border box size of an element. This is potentially expensive in IE
        1491 * if the document is CSS1Compat mode
        1492 * @param {Element} element The element to set the size on.
        1493 * @param {goog.math.Size} size The new size.
        1494 */
        1495goog.style.setBorderBoxSize = function(element, size) {
        1496 var doc = goog.dom.getOwnerDocument(element);
        1497 var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
        1498
        1499 if (goog.userAgent.IE &&
        1500 !goog.userAgent.isVersionOrHigher('10') &&
        1501 (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
        1502 var style = element.style;
        1503 if (isCss1CompatMode) {
        1504 var paddingBox = goog.style.getPaddingBox(element);
        1505 var borderBox = goog.style.getBorderBox(element);
        1506 style.pixelWidth = size.width - borderBox.left - paddingBox.left -
        1507 paddingBox.right - borderBox.right;
        1508 style.pixelHeight = size.height - borderBox.top - paddingBox.top -
        1509 paddingBox.bottom - borderBox.bottom;
        1510 } else {
        1511 style.pixelWidth = size.width;
        1512 style.pixelHeight = size.height;
        1513 }
        1514 } else {
        1515 goog.style.setBoxSizingSize_(element, size, 'border-box');
        1516 }
        1517};
        1518
        1519
        1520/**
        1521 * Gets the content box size for an element. This is potentially expensive in
        1522 * all browsers.
        1523 * @param {Element} element The element to get the size for.
        1524 * @return {!goog.math.Size} The content box size.
        1525 */
        1526goog.style.getContentBoxSize = function(element) {
        1527 var doc = goog.dom.getOwnerDocument(element);
        1528 var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;
        1529 if (ieCurrentStyle &&
        1530 goog.dom.getDomHelper(doc).isCss1CompatMode() &&
        1531 ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&
        1532 !ieCurrentStyle.boxSizing) {
        1533 // If IE in CSS1Compat mode than just use the width and height.
        1534 // If we have a boxSizing then fall back on measuring the borders etc.
        1535 var width = goog.style.getIePixelValue_(element, ieCurrentStyle.width,
        1536 'width', 'pixelWidth');
        1537 var height = goog.style.getIePixelValue_(element, ieCurrentStyle.height,
        1538 'height', 'pixelHeight');
        1539 return new goog.math.Size(width, height);
        1540 } else {
        1541 var borderBoxSize = goog.style.getBorderBoxSize(element);
        1542 var paddingBox = goog.style.getPaddingBox(element);
        1543 var borderBox = goog.style.getBorderBox(element);
        1544 return new goog.math.Size(borderBoxSize.width -
        1545 borderBox.left - paddingBox.left -
        1546 paddingBox.right - borderBox.right,
        1547 borderBoxSize.height -
        1548 borderBox.top - paddingBox.top -
        1549 paddingBox.bottom - borderBox.bottom);
        1550 }
        1551};
        1552
        1553
        1554/**
        1555 * Sets the content box size of an element. This is potentially expensive in IE
        1556 * if the document is BackCompat mode.
        1557 * @param {Element} element The element to set the size on.
        1558 * @param {goog.math.Size} size The new size.
        1559 */
        1560goog.style.setContentBoxSize = function(element, size) {
        1561 var doc = goog.dom.getOwnerDocument(element);
        1562 var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
        1563 if (goog.userAgent.IE &&
        1564 !goog.userAgent.isVersionOrHigher('10') &&
        1565 (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
        1566 var style = element.style;
        1567 if (isCss1CompatMode) {
        1568 style.pixelWidth = size.width;
        1569 style.pixelHeight = size.height;
        1570 } else {
        1571 var paddingBox = goog.style.getPaddingBox(element);
        1572 var borderBox = goog.style.getBorderBox(element);
        1573 style.pixelWidth = size.width + borderBox.left + paddingBox.left +
        1574 paddingBox.right + borderBox.right;
        1575 style.pixelHeight = size.height + borderBox.top + paddingBox.top +
        1576 paddingBox.bottom + borderBox.bottom;
        1577 }
        1578 } else {
        1579 goog.style.setBoxSizingSize_(element, size, 'content-box');
        1580 }
        1581};
        1582
        1583
        1584/**
        1585 * Helper function that sets the box sizing as well as the width and height
        1586 * @param {Element} element The element to set the size on.
        1587 * @param {goog.math.Size} size The new size to set.
        1588 * @param {string} boxSizing The box-sizing value.
        1589 * @private
        1590 */
        1591goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {
        1592 var style = element.style;
        1593 if (goog.userAgent.GECKO) {
        1594 style.MozBoxSizing = boxSizing;
        1595 } else if (goog.userAgent.WEBKIT) {
        1596 style.WebkitBoxSizing = boxSizing;
        1597 } else {
        1598 // Includes IE8 and Opera 9.50+
        1599 style.boxSizing = boxSizing;
        1600 }
        1601
        1602 // Setting this to a negative value will throw an exception on IE
        1603 // (and doesn't do anything different than setting it to 0).
        1604 style.width = Math.max(size.width, 0) + 'px';
        1605 style.height = Math.max(size.height, 0) + 'px';
        1606};
        1607
        1608
        1609/**
        1610 * IE specific function that converts a non pixel unit to pixels.
        1611 * @param {Element} element The element to convert the value for.
        1612 * @param {string} value The current value as a string. The value must not be
        1613 * ''.
        1614 * @param {string} name The CSS property name to use for the converstion. This
        1615 * should be 'left', 'top', 'width' or 'height'.
        1616 * @param {string} pixelName The CSS pixel property name to use to get the
        1617 * value in pixels.
        1618 * @return {number} The value in pixels.
        1619 * @private
        1620 */
        1621goog.style.getIePixelValue_ = function(element, value, name, pixelName) {
        1622 // Try if we already have a pixel value. IE does not do half pixels so we
        1623 // only check if it matches a number followed by 'px'.
        1624 if (/^\d+px?$/.test(value)) {
        1625 return parseInt(value, 10);
        1626 } else {
        1627 var oldStyleValue = element.style[name];
        1628 var oldRuntimeValue = element.runtimeStyle[name];
        1629 // set runtime style to prevent changes
        1630 element.runtimeStyle[name] = element.currentStyle[name];
        1631 element.style[name] = value;
        1632 var pixelValue = element.style[pixelName];
        1633 // restore
        1634 element.style[name] = oldStyleValue;
        1635 element.runtimeStyle[name] = oldRuntimeValue;
        1636 return pixelValue;
        1637 }
        1638};
        1639
        1640
        1641/**
        1642 * Helper function for getting the pixel padding or margin for IE.
        1643 * @param {Element} element The element to get the padding for.
        1644 * @param {string} propName The property name.
        1645 * @return {number} The pixel padding.
        1646 * @private
        1647 */
        1648goog.style.getIePixelDistance_ = function(element, propName) {
        1649 var value = goog.style.getCascadedStyle(element, propName);
        1650 return value ?
        1651 goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') : 0;
        1652};
        1653
        1654
        1655/**
        1656 * Gets the computed paddings or margins (on all sides) in pixels.
        1657 * @param {Element} element The element to get the padding for.
        1658 * @param {string} stylePrefix Pass 'padding' to retrieve the padding box,
        1659 * or 'margin' to retrieve the margin box.
        1660 * @return {!goog.math.Box} The computed paddings or margins.
        1661 * @private
        1662 */
        1663goog.style.getBox_ = function(element, stylePrefix) {
        1664 if (goog.userAgent.IE) {
        1665 var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');
        1666 var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');
        1667 var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');
        1668 var bottom = goog.style.getIePixelDistance_(
        1669 element, stylePrefix + 'Bottom');
        1670 return new goog.math.Box(top, right, bottom, left);
        1671 } else {
        1672 // On non-IE browsers, getComputedStyle is always non-null.
        1673 var left = goog.style.getComputedStyle(element, stylePrefix + 'Left');
        1674 var right = goog.style.getComputedStyle(element, stylePrefix + 'Right');
        1675 var top = goog.style.getComputedStyle(element, stylePrefix + 'Top');
        1676 var bottom = goog.style.getComputedStyle(element, stylePrefix + 'Bottom');
        1677
        1678 // NOTE(arv): Gecko can return floating point numbers for the computed
        1679 // style values.
        1680 return new goog.math.Box(parseFloat(top),
        1681 parseFloat(right),
        1682 parseFloat(bottom),
        1683 parseFloat(left));
        1684 }
        1685};
        1686
        1687
        1688/**
        1689 * Gets the computed paddings (on all sides) in pixels.
        1690 * @param {Element} element The element to get the padding for.
        1691 * @return {!goog.math.Box} The computed paddings.
        1692 */
        1693goog.style.getPaddingBox = function(element) {
        1694 return goog.style.getBox_(element, 'padding');
        1695};
        1696
        1697
        1698/**
        1699 * Gets the computed margins (on all sides) in pixels.
        1700 * @param {Element} element The element to get the margins for.
        1701 * @return {!goog.math.Box} The computed margins.
        1702 */
        1703goog.style.getMarginBox = function(element) {
        1704 return goog.style.getBox_(element, 'margin');
        1705};
        1706
        1707
        1708/**
        1709 * A map used to map the border width keywords to a pixel width.
        1710 * @type {Object}
        1711 * @private
        1712 */
        1713goog.style.ieBorderWidthKeywords_ = {
        1714 'thin': 2,
        1715 'medium': 4,
        1716 'thick': 6
        1717};
        1718
        1719
        1720/**
        1721 * Helper function for IE to get the pixel border.
        1722 * @param {Element} element The element to get the pixel border for.
        1723 * @param {string} prop The part of the property name.
        1724 * @return {number} The value in pixels.
        1725 * @private
        1726 */
        1727goog.style.getIePixelBorder_ = function(element, prop) {
        1728 if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {
        1729 return 0;
        1730 }
        1731 var width = goog.style.getCascadedStyle(element, prop + 'Width');
        1732 if (width in goog.style.ieBorderWidthKeywords_) {
        1733 return goog.style.ieBorderWidthKeywords_[width];
        1734 }
        1735 return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');
        1736};
        1737
        1738
        1739/**
        1740 * Gets the computed border widths (on all sides) in pixels
        1741 * @param {Element} element The element to get the border widths for.
        1742 * @return {!goog.math.Box} The computed border widths.
        1743 */
        1744goog.style.getBorderBox = function(element) {
        1745 if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
        1746 var left = goog.style.getIePixelBorder_(element, 'borderLeft');
        1747 var right = goog.style.getIePixelBorder_(element, 'borderRight');
        1748 var top = goog.style.getIePixelBorder_(element, 'borderTop');
        1749 var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');
        1750 return new goog.math.Box(top, right, bottom, left);
        1751 } else {
        1752 // On non-IE browsers, getComputedStyle is always non-null.
        1753 var left = goog.style.getComputedStyle(element, 'borderLeftWidth');
        1754 var right = goog.style.getComputedStyle(element, 'borderRightWidth');
        1755 var top = goog.style.getComputedStyle(element, 'borderTopWidth');
        1756 var bottom = goog.style.getComputedStyle(element, 'borderBottomWidth');
        1757
        1758 return new goog.math.Box(parseFloat(top),
        1759 parseFloat(right),
        1760 parseFloat(bottom),
        1761 parseFloat(left));
        1762 }
        1763};
        1764
        1765
        1766/**
        1767 * Returns the font face applied to a given node. Opera and IE should return
        1768 * the font actually displayed. Firefox returns the author's most-preferred
        1769 * font (whether the browser is capable of displaying it or not.)
        1770 * @param {Element} el The element whose font family is returned.
        1771 * @return {string} The font family applied to el.
        1772 */
        1773goog.style.getFontFamily = function(el) {
        1774 var doc = goog.dom.getOwnerDocument(el);
        1775 var font = '';
        1776 // The moveToElementText method from the TextRange only works if the element
        1777 // is attached to the owner document.
        1778 if (doc.body.createTextRange && goog.dom.contains(doc, el)) {
        1779 var range = doc.body.createTextRange();
        1780 range.moveToElementText(el);
        1781 /** @preserveTry */
        1782 try {
        1783 font = range.queryCommandValue('FontName');
        1784 } catch (e) {
        1785 // This is a workaround for a awkward exception.
        1786 // On some IE, there is an exception coming from it.
        1787 // The error description from this exception is:
        1788 // This window has already been registered as a drop target
        1789 // This is bogus description, likely due to a bug in ie.
        1790 font = '';
        1791 }
        1792 }
        1793 if (!font) {
        1794 // Note if for some reason IE can't derive FontName with a TextRange, we
        1795 // fallback to using currentStyle
        1796 font = goog.style.getStyle_(el, 'fontFamily');
        1797 }
        1798
        1799 // Firefox returns the applied font-family string (author's list of
        1800 // preferred fonts.) We want to return the most-preferred font, in lieu of
        1801 // the *actually* applied font.
        1802 var fontsArray = font.split(',');
        1803 if (fontsArray.length > 1) font = fontsArray[0];
        1804
        1805 // Sanitize for x-browser consistency:
        1806 // Strip quotes because browsers aren't consistent with how they're
        1807 // applied; Opera always encloses, Firefox sometimes, and IE never.
        1808 return goog.string.stripQuotes(font, '"\'');
        1809};
        1810
        1811
        1812/**
        1813 * Regular expression used for getLengthUnits.
        1814 * @type {RegExp}
        1815 * @private
        1816 */
        1817goog.style.lengthUnitRegex_ = /[^\d]+$/;
        1818
        1819
        1820/**
        1821 * Returns the units used for a CSS length measurement.
        1822 * @param {string} value A CSS length quantity.
        1823 * @return {?string} The units of measurement.
        1824 */
        1825goog.style.getLengthUnits = function(value) {
        1826 var units = value.match(goog.style.lengthUnitRegex_);
        1827 return units && units[0] || null;
        1828};
        1829
        1830
        1831/**
        1832 * Map of absolute CSS length units
        1833 * @type {Object}
        1834 * @private
        1835 */
        1836goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {
        1837 'cm' : 1,
        1838 'in' : 1,
        1839 'mm' : 1,
        1840 'pc' : 1,
        1841 'pt' : 1
        1842};
        1843
        1844
        1845/**
        1846 * Map of relative CSS length units that can be accurately converted to px
        1847 * font-size values using getIePixelValue_. Only units that are defined in
        1848 * relation to a font size are convertible (%, small, etc. are not).
        1849 * @type {Object}
        1850 * @private
        1851 */
        1852goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {
        1853 'em' : 1,
        1854 'ex' : 1
        1855};
        1856
        1857
        1858/**
        1859 * Returns the font size, in pixels, of text in an element.
        1860 * @param {Element} el The element whose font size is returned.
        1861 * @return {number} The font size (in pixels).
        1862 */
        1863goog.style.getFontSize = function(el) {
        1864 var fontSize = goog.style.getStyle_(el, 'fontSize');
        1865 var sizeUnits = goog.style.getLengthUnits(fontSize);
        1866 if (fontSize && 'px' == sizeUnits) {
        1867 // NOTE(user): This could be parseFloat instead, but IE doesn't return
        1868 // decimal fractions in getStyle_ and Firefox reports the fractions, but
        1869 // ignores them when rendering. Interestingly enough, when we force the
        1870 // issue and size something to e.g., 50% of 25px, the browsers round in
        1871 // opposite directions with Firefox reporting 12px and IE 13px. I punt.
        1872 return parseInt(fontSize, 10);
        1873 }
        1874
        1875 // In IE, we can convert absolute length units to a px value using
        1876 // goog.style.getIePixelValue_. Units defined in relation to a font size
        1877 // (em, ex) are applied relative to the element's parentNode and can also
        1878 // be converted.
        1879 if (goog.userAgent.IE) {
        1880 if (sizeUnits in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {
        1881 return goog.style.getIePixelValue_(el,
        1882 fontSize,
        1883 'left',
        1884 'pixelLeft');
        1885 } else if (el.parentNode &&
        1886 el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&
        1887 sizeUnits in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {
        1888 // Check the parent size - if it is the same it means the relative size
        1889 // value is inherited and we therefore don't want to count it twice. If
        1890 // it is different, this element either has explicit style or has a CSS
        1891 // rule applying to it.
        1892 var parentElement = /** @type {!Element} */ (el.parentNode);
        1893 var parentSize = goog.style.getStyle_(parentElement, 'fontSize');
        1894 return goog.style.getIePixelValue_(parentElement,
        1895 fontSize == parentSize ?
        1896 '1em' : fontSize,
        1897 'left',
        1898 'pixelLeft');
        1899 }
        1900 }
        1901
        1902 // Sometimes we can't cleanly find the font size (some units relative to a
        1903 // node's parent's font size are difficult: %, smaller et al), so we create
        1904 // an invisible, absolutely-positioned span sized to be the height of an 'M'
        1905 // rendered in its parent's (i.e., our target element's) font size. This is
        1906 // the definition of CSS's font size attribute.
        1907 var sizeElement = goog.dom.createDom(
        1908 goog.dom.TagName.SPAN,
        1909 {'style': 'visibility:hidden;position:absolute;' +
        1910 'line-height:0;padding:0;margin:0;border:0;height:1em;'});
        1911 goog.dom.appendChild(el, sizeElement);
        1912 fontSize = sizeElement.offsetHeight;
        1913 goog.dom.removeNode(sizeElement);
        1914
        1915 return fontSize;
        1916};
        1917
        1918
        1919/**
        1920 * Parses a style attribute value. Converts CSS property names to camel case.
        1921 * @param {string} value The style attribute value.
        1922 * @return {!Object} Map of CSS properties to string values.
        1923 */
        1924goog.style.parseStyleAttribute = function(value) {
        1925 var result = {};
        1926 goog.array.forEach(value.split(/\s*;\s*/), function(pair) {
        1927 var keyValue = pair.match(/\s*([\w-]+)\s*\:(.+)/);
        1928 if (keyValue) {
        1929 var styleName = keyValue[1];
        1930 var styleValue = goog.string.trim(keyValue[2]);
        1931 result[goog.string.toCamelCase(styleName.toLowerCase())] = styleValue;
        1932 }
        1933 });
        1934 return result;
        1935};
        1936
        1937
        1938/**
        1939 * Reverse of parseStyleAttribute; that is, takes a style object and returns the
        1940 * corresponding attribute value. Converts camel case property names to proper
        1941 * CSS selector names.
        1942 * @param {Object} obj Map of CSS properties to values.
        1943 * @return {string} The style attribute value.
        1944 */
        1945goog.style.toStyleAttribute = function(obj) {
        1946 var buffer = [];
        1947 goog.object.forEach(obj, function(value, key) {
        1948 buffer.push(goog.string.toSelectorCase(key), ':', value, ';');
        1949 });
        1950 return buffer.join('');
        1951};
        1952
        1953
        1954/**
        1955 * Sets CSS float property on an element.
        1956 * @param {Element} el The element to set float property on.
        1957 * @param {string} value The value of float CSS property to set on this element.
        1958 */
        1959goog.style.setFloat = function(el, value) {
        1960 el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value;
        1961};
        1962
        1963
        1964/**
        1965 * Gets value of explicitly-set float CSS property on an element.
        1966 * @param {Element} el The element to get float property of.
        1967 * @return {string} The value of explicitly-set float CSS property on this
        1968 * element.
        1969 */
        1970goog.style.getFloat = function(el) {
        1971 return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || '';
        1972};
        1973
        1974
        1975/**
        1976 * Returns the scroll bar width (represents the width of both horizontal
        1977 * and vertical scroll).
        1978 *
        1979 * @param {string=} opt_className An optional class name (or names) to apply
        1980 * to the invisible div created to measure the scrollbar. This is necessary
        1981 * if some scrollbars are styled differently than others.
        1982 * @return {number} The scroll bar width in px.
        1983 */
        1984goog.style.getScrollbarWidth = function(opt_className) {
        1985 // Add two hidden divs. The child div is larger than the parent and
        1986 // forces scrollbars to appear on it.
        1987 // Using overflow:scroll does not work consistently with scrollbars that
        1988 // are styled with ::-webkit-scrollbar.
        1989 var outerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
        1990 if (opt_className) {
        1991 outerDiv.className = opt_className;
        1992 }
        1993 outerDiv.style.cssText = 'overflow:auto;' +
        1994 'position:absolute;top:0;width:100px;height:100px';
        1995 var innerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
        1996 goog.style.setSize(innerDiv, '200px', '200px');
        1997 outerDiv.appendChild(innerDiv);
        1998 goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);
        1999 var width = outerDiv.offsetWidth - outerDiv.clientWidth;
        2000 goog.dom.removeNode(outerDiv);
        2001 return width;
        2002};
        2003
        2004
        2005/**
        2006 * Regular expression to extract x and y translation components from a CSS
        2007 * transform Matrix representation.
        2008 *
        2009 * @type {!RegExp}
        2010 * @const
        2011 * @private
        2012 */
        2013goog.style.MATRIX_TRANSLATION_REGEX_ =
        2014 new RegExp('matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +
        2015 '[0-9\\.\\-]+, [0-9\\.\\-]+, ' +
        2016 '([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');
        2017
        2018
        2019/**
        2020 * Returns the x,y translation component of any CSS transforms applied to the
        2021 * element, in pixels.
        2022 *
        2023 * @param {!Element} element The element to get the translation of.
        2024 * @return {!goog.math.Coordinate} The CSS translation of the element in px.
        2025 */
        2026goog.style.getCssTranslation = function(element) {
        2027 var transform = goog.style.getComputedTransform(element);
        2028 if (!transform) {
        2029 return new goog.math.Coordinate(0, 0);
        2030 }
        2031 var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);
        2032 if (!matches) {
        2033 return new goog.math.Coordinate(0, 0);
        2034 }
        2035 return new goog.math.Coordinate(parseFloat(matches[1]),
        2036 parseFloat(matches[2]));
        2037};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/asserts.js.src.html b/docs/source/lib/goog/testing/asserts.js.src.html new file mode 100644 index 0000000..0bcf36a --- /dev/null +++ b/docs/source/lib/goog/testing/asserts.js.src.html @@ -0,0 +1 @@ +asserts.js

        lib/goog/testing/asserts.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.
        14goog.provide('goog.testing.JsUnitException');
        15goog.provide('goog.testing.asserts');
        16goog.provide('goog.testing.asserts.ArrayLike');
        17
        18goog.require('goog.testing.stacktrace');
        19
        20// TODO(user): Copied from JsUnit with some small modifications, we should
        21// reimplement the asserters.
        22
        23
        24/**
        25 * @typedef {Array|NodeList|Arguments|{length: number}}
        26 */
        27goog.testing.asserts.ArrayLike;
        28
        29var DOUBLE_EQUALITY_PREDICATE = function(var1, var2) {
        30 return var1 == var2;
        31};
        32var JSUNIT_UNDEFINED_VALUE = void 0;
        33var TO_STRING_EQUALITY_PREDICATE = function(var1, var2) {
        34 return var1.toString() === var2.toString();
        35};
        36
        37/** @typedef {function(?, ?):boolean} */
        38var PredicateFunctionType;
        39
        40/**
        41 * @const {{
        42 * String : PredicateFunctionType,
        43 * Number : PredicateFunctionType,
        44 * Boolean : PredicateFunctionType,
        45 * Date : PredicateFunctionType,
        46 * RegExp : PredicateFunctionType,
        47 * Function : PredicateFunctionType
        48 * }}
        49 */
        50var PRIMITIVE_EQUALITY_PREDICATES = {
        51 'String': DOUBLE_EQUALITY_PREDICATE,
        52 'Number': DOUBLE_EQUALITY_PREDICATE,
        53 'Boolean': DOUBLE_EQUALITY_PREDICATE,
        54 'Date': function(date1, date2) {
        55 return date1.getTime() == date2.getTime();
        56 },
        57 'RegExp': TO_STRING_EQUALITY_PREDICATE,
        58 'Function': TO_STRING_EQUALITY_PREDICATE
        59};
        60
        61
        62/**
        63 * Compares equality of two numbers, allowing them to differ up to a given
        64 * tolerance.
        65 * @param {number} var1 A number.
        66 * @param {number} var2 A number.
        67 * @param {number} tolerance the maximum allowed difference.
        68 * @return {boolean} Whether the two variables are sufficiently close.
        69 * @private
        70 */
        71goog.testing.asserts.numberRoughEqualityPredicate_ = function(
        72 var1, var2, tolerance) {
        73 return Math.abs(var1 - var2) <= tolerance;
        74};
        75
        76
        77/**
        78 * @type {Object<string, function(*, *, number): boolean>}
        79 * @private
        80 */
        81goog.testing.asserts.primitiveRoughEqualityPredicates_ = {
        82 'Number': goog.testing.asserts.numberRoughEqualityPredicate_
        83};
        84
        85
        86var _trueTypeOf = function(something) {
        87 var result = typeof something;
        88 try {
        89 switch (result) {
        90 case 'string':
        91 break;
        92 case 'boolean':
        93 break;
        94 case 'number':
        95 break;
        96 case 'object':
        97 if (something == null) {
        98 result = 'null';
        99 break;
        100 }
        101 case 'function':
        102 switch (something.constructor) {
        103 case new String('').constructor:
        104 result = 'String';
        105 break;
        106 case new Boolean(true).constructor:
        107 result = 'Boolean';
        108 break;
        109 case new Number(0).constructor:
        110 result = 'Number';
        111 break;
        112 case new Array().constructor:
        113 result = 'Array';
        114 break;
        115 case new RegExp().constructor:
        116 result = 'RegExp';
        117 break;
        118 case new Date().constructor:
        119 result = 'Date';
        120 break;
        121 case Function:
        122 result = 'Function';
        123 break;
        124 default:
        125 var m = something.constructor.toString().match(
        126 /function\s*([^( ]+)\(/);
        127 if (m) {
        128 result = m[1];
        129 } else {
        130 break;
        131 }
        132 }
        133 break;
        134 }
        135 } catch (e) {
        136
        137 } finally {
        138 result = result.substr(0, 1).toUpperCase() + result.substr(1);
        139 }
        140 return result;
        141};
        142
        143var _displayStringForValue = function(aVar) {
        144 var result;
        145 try {
        146 result = '<' + String(aVar) + '>';
        147 } catch (ex) {
        148 result = '<toString failed: ' + ex.message + '>';
        149 // toString does not work on this object :-(
        150 }
        151 if (!(aVar === null || aVar === JSUNIT_UNDEFINED_VALUE)) {
        152 result += ' (' + _trueTypeOf(aVar) + ')';
        153 }
        154 return result;
        155};
        156
        157var fail = function(failureMessage) {
        158 goog.testing.asserts.raiseException('Call to fail()', failureMessage);
        159};
        160
        161var argumentsIncludeComments = function(expectedNumberOfNonCommentArgs, args) {
        162 return args.length == expectedNumberOfNonCommentArgs + 1;
        163};
        164
        165var commentArg = function(expectedNumberOfNonCommentArgs, args) {
        166 if (argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) {
        167 return args[0];
        168 }
        169
        170 return null;
        171};
        172
        173var nonCommentArg = function(desiredNonCommentArgIndex,
        174 expectedNumberOfNonCommentArgs, args) {
        175 return argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ?
        176 args[desiredNonCommentArgIndex] :
        177 args[desiredNonCommentArgIndex - 1];
        178};
        179
        180var _validateArguments = function(expectedNumberOfNonCommentArgs, args) {
        181 var valid = args.length == expectedNumberOfNonCommentArgs ||
        182 args.length == expectedNumberOfNonCommentArgs + 1 &&
        183 goog.isString(args[0]);
        184 _assert(null, valid, 'Incorrect arguments passed to assert function');
        185};
        186
        187var _assert = function(comment, booleanValue, failureMessage) {
        188 if (!booleanValue) {
        189 goog.testing.asserts.raiseException(comment, failureMessage);
        190 }
        191};
        192
        193
        194/**
        195 * @param {*} expected The expected value.
        196 * @param {*} actual The actual value.
        197 * @return {string} A failure message of the values don't match.
        198 * @private
        199 */
        200goog.testing.asserts.getDefaultErrorMsg_ = function(expected, actual) {
        201 var msg = 'Expected ' + _displayStringForValue(expected) + ' but was ' +
        202 _displayStringForValue(actual);
        203 if ((typeof expected == 'string') && (typeof actual == 'string')) {
        204 // Try to find a human-readable difference.
        205 var limit = Math.min(expected.length, actual.length);
        206 var commonPrefix = 0;
        207 while (commonPrefix < limit &&
        208 expected.charAt(commonPrefix) == actual.charAt(commonPrefix)) {
        209 commonPrefix++;
        210 }
        211
        212 var commonSuffix = 0;
        213 while (commonSuffix < limit &&
        214 expected.charAt(expected.length - commonSuffix - 1) ==
        215 actual.charAt(actual.length - commonSuffix - 1)) {
        216 commonSuffix++;
        217 }
        218
        219 if (commonPrefix + commonSuffix > limit) {
        220 commonSuffix = 0;
        221 }
        222
        223 if (commonPrefix > 2 || commonSuffix > 2) {
        224 var printString = function(str) {
        225 var startIndex = Math.max(0, commonPrefix - 2);
        226 var endIndex = Math.min(str.length, str.length - (commonSuffix - 2));
        227 return (startIndex > 0 ? '...' : '') +
        228 str.substring(startIndex, endIndex) +
        229 (endIndex < str.length ? '...' : '');
        230 };
        231
        232 msg += '\nDifference was at position ' + commonPrefix +
        233 '. Expected [' + printString(expected) +
        234 '] vs. actual [' + printString(actual) + ']';
        235 }
        236 }
        237 return msg;
        238};
        239
        240
        241/**
        242 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        243 * @param {*=} opt_b The value to assert (2 args only).
        244 */
        245var assert = function(a, opt_b) {
        246 _validateArguments(1, arguments);
        247 var comment = commentArg(1, arguments);
        248 var booleanValue = nonCommentArg(1, 1, arguments);
        249
        250 _assert(comment, goog.isBoolean(booleanValue),
        251 'Bad argument to assert(boolean)');
        252 _assert(comment, booleanValue, 'Call to assert(boolean) with false');
        253};
        254
        255
        256/**
        257 * Asserts that the function throws an error.
        258 *
        259 * @param {!(string|Function)} a The assertion comment or the function to call.
        260 * @param {!Function=} opt_b The function to call (if the first argument of
        261 * {@code assertThrows} was the comment).
        262 * @return {*} The error thrown by the function.
        263 * @throws {goog.testing.JsUnitException} If the assertion failed.
        264 */
        265var assertThrows = function(a, opt_b) {
        266 _validateArguments(1, arguments);
        267 var func = nonCommentArg(1, 1, arguments);
        268 var comment = commentArg(1, arguments);
        269 _assert(comment, typeof func == 'function',
        270 'Argument passed to assertThrows is not a function');
        271
        272 try {
        273 func();
        274 } catch (e) {
        275 if (e && goog.isString(e['stacktrace']) && goog.isString(e['message'])) {
        276 // Remove the stack trace appended to the error message by Opera 10.0
        277 var startIndex = e['message'].length - e['stacktrace'].length;
        278 if (e['message'].indexOf(e['stacktrace'], startIndex) == startIndex) {
        279 e['message'] = e['message'].substr(0, startIndex - 14);
        280 }
        281 }
        282 return e;
        283 }
        284 goog.testing.asserts.raiseException(comment,
        285 'No exception thrown from function passed to assertThrows');
        286};
        287
        288
        289/**
        290 * Asserts that the function does not throw an error.
        291 *
        292 * @param {!(string|Function)} a The assertion comment or the function to call.
        293 * @param {!Function=} opt_b The function to call (if the first argument of
        294 * {@code assertNotThrows} was the comment).
        295 * @return {*} The return value of the function.
        296 * @throws {goog.testing.JsUnitException} If the assertion failed.
        297 */
        298var assertNotThrows = function(a, opt_b) {
        299 _validateArguments(1, arguments);
        300 var comment = commentArg(1, arguments);
        301 var func = nonCommentArg(1, 1, arguments);
        302 _assert(comment, typeof func == 'function',
        303 'Argument passed to assertNotThrows is not a function');
        304
        305 try {
        306 return func();
        307 } catch (e) {
        308 comment = comment ? (comment + '\n') : '';
        309 comment += 'A non expected exception was thrown from function passed to ' +
        310 'assertNotThrows';
        311 // Some browsers don't have a stack trace so at least have the error
        312 // description.
        313 var stackTrace = e['stack'] || e['stacktrace'] || e.toString();
        314 goog.testing.asserts.raiseException(comment, stackTrace);
        315 }
        316};
        317
        318
        319/**
        320 * Asserts that the given callback function results in a JsUnitException when
        321 * called, and that the resulting failure message matches the given expected
        322 * message.
        323 * @param {function() : void} callback Function to be run expected to result
        324 * in a JsUnitException (usually contains a call to an assert).
        325 * @param {string=} opt_expectedMessage Failure message expected to be given
        326 * with the exception.
        327 */
        328var assertThrowsJsUnitException = function(callback, opt_expectedMessage) {
        329 var failed = false;
        330 try {
        331 goog.testing.asserts.callWithoutLogging(callback);
        332 } catch (ex) {
        333 if (!ex.isJsUnitException) {
        334 fail('Expected a JsUnitException');
        335 }
        336 if (typeof opt_expectedMessage != 'undefined' &&
        337 ex.message != opt_expectedMessage) {
        338 fail('Expected message [' + opt_expectedMessage + '] but got [' +
        339 ex.message + ']');
        340 }
        341 failed = true;
        342 }
        343 if (!failed) {
        344 fail('Expected a failure: ' + opt_expectedMessage);
        345 }
        346};
        347
        348
        349/**
        350 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        351 * @param {*=} opt_b The value to assert (2 args only).
        352 */
        353var assertTrue = function(a, opt_b) {
        354 _validateArguments(1, arguments);
        355 var comment = commentArg(1, arguments);
        356 var booleanValue = nonCommentArg(1, 1, arguments);
        357
        358 _assert(comment, goog.isBoolean(booleanValue),
        359 'Bad argument to assertTrue(boolean)');
        360 _assert(comment, booleanValue, 'Call to assertTrue(boolean) with false');
        361};
        362
        363
        364/**
        365 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        366 * @param {*=} opt_b The value to assert (2 args only).
        367 */
        368var assertFalse = function(a, opt_b) {
        369 _validateArguments(1, arguments);
        370 var comment = commentArg(1, arguments);
        371 var booleanValue = nonCommentArg(1, 1, arguments);
        372
        373 _assert(comment, goog.isBoolean(booleanValue),
        374 'Bad argument to assertFalse(boolean)');
        375 _assert(comment, !booleanValue, 'Call to assertFalse(boolean) with true');
        376};
        377
        378
        379/**
        380 * @param {*} a The expected value (2 args) or the debug message (3 args).
        381 * @param {*} b The actual value (2 args) or the expected value (3 args).
        382 * @param {*=} opt_c The actual value (3 args only).
        383 */
        384var assertEquals = function(a, b, opt_c) {
        385 _validateArguments(2, arguments);
        386 var var1 = nonCommentArg(1, 2, arguments);
        387 var var2 = nonCommentArg(2, 2, arguments);
        388 _assert(commentArg(2, arguments), var1 === var2,
        389 goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
        390};
        391
        392
        393/**
        394 * @param {*} a The expected value (2 args) or the debug message (3 args).
        395 * @param {*} b The actual value (2 args) or the expected value (3 args).
        396 * @param {*=} opt_c The actual value (3 args only).
        397 */
        398var assertNotEquals = function(a, b, opt_c) {
        399 _validateArguments(2, arguments);
        400 var var1 = nonCommentArg(1, 2, arguments);
        401 var var2 = nonCommentArg(2, 2, arguments);
        402 _assert(commentArg(2, arguments), var1 !== var2,
        403 'Expected not to be ' + _displayStringForValue(var2));
        404};
        405
        406
        407/**
        408 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        409 * @param {*=} opt_b The value to assert (2 args only).
        410 */
        411var assertNull = function(a, opt_b) {
        412 _validateArguments(1, arguments);
        413 var aVar = nonCommentArg(1, 1, arguments);
        414 _assert(commentArg(1, arguments), aVar === null,
        415 goog.testing.asserts.getDefaultErrorMsg_(null, aVar));
        416};
        417
        418
        419/**
        420 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        421 * @param {*=} opt_b The value to assert (2 args only).
        422 */
        423var assertNotNull = function(a, opt_b) {
        424 _validateArguments(1, arguments);
        425 var aVar = nonCommentArg(1, 1, arguments);
        426 _assert(commentArg(1, arguments), aVar !== null,
        427 'Expected not to be ' + _displayStringForValue(null));
        428};
        429
        430
        431/**
        432 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        433 * @param {*=} opt_b The value to assert (2 args only).
        434 */
        435var assertUndefined = function(a, opt_b) {
        436 _validateArguments(1, arguments);
        437 var aVar = nonCommentArg(1, 1, arguments);
        438 _assert(commentArg(1, arguments), aVar === JSUNIT_UNDEFINED_VALUE,
        439 goog.testing.asserts.getDefaultErrorMsg_(JSUNIT_UNDEFINED_VALUE, aVar));
        440};
        441
        442
        443/**
        444 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        445 * @param {*=} opt_b The value to assert (2 args only).
        446 */
        447var assertNotUndefined = function(a, opt_b) {
        448 _validateArguments(1, arguments);
        449 var aVar = nonCommentArg(1, 1, arguments);
        450 _assert(commentArg(1, arguments), aVar !== JSUNIT_UNDEFINED_VALUE,
        451 'Expected not to be ' + _displayStringForValue(JSUNIT_UNDEFINED_VALUE));
        452};
        453
        454
        455/**
        456 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        457 * @param {*=} opt_b The value to assert (2 args only).
        458 */
        459var assertNotNullNorUndefined = function(a, opt_b) {
        460 _validateArguments(1, arguments);
        461 assertNotNull.apply(null, arguments);
        462 assertNotUndefined.apply(null, arguments);
        463};
        464
        465
        466/**
        467 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        468 * @param {*=} opt_b The value to assert (2 args only).
        469 */
        470var assertNonEmptyString = function(a, opt_b) {
        471 _validateArguments(1, arguments);
        472 var aVar = nonCommentArg(1, 1, arguments);
        473 _assert(commentArg(1, arguments),
        474 aVar !== JSUNIT_UNDEFINED_VALUE && aVar !== null &&
        475 typeof aVar == 'string' && aVar !== '',
        476 'Expected non-empty string but was ' + _displayStringForValue(aVar));
        477};
        478
        479
        480/**
        481 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        482 * @param {*=} opt_b The value to assert (2 args only).
        483 */
        484var assertNaN = function(a, opt_b) {
        485 _validateArguments(1, arguments);
        486 var aVar = nonCommentArg(1, 1, arguments);
        487 _assert(commentArg(1, arguments), isNaN(aVar), 'Expected NaN');
        488};
        489
        490
        491/**
        492 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        493 * @param {*=} opt_b The value to assert (2 args only).
        494 */
        495var assertNotNaN = function(a, opt_b) {
        496 _validateArguments(1, arguments);
        497 var aVar = nonCommentArg(1, 1, arguments);
        498 _assert(commentArg(1, arguments), !isNaN(aVar), 'Expected not NaN');
        499};
        500
        501
        502/**
        503 * Runs a function in an environment where test failures are not logged. This is
        504 * useful for testing test code, where failures can be a normal part of a test.
        505 * @param {function() : void} fn Function to run without logging failures.
        506 */
        507goog.testing.asserts.callWithoutLogging = function(fn) {
        508 var testRunner = goog.global['G_testRunner'];
        509 var oldLogTestFailure = testRunner['logTestFailure'];
        510 try {
        511 // Any failures in the callback shouldn't be recorded.
        512 testRunner['logTestFailure'] = undefined;
        513 fn();
        514 } finally {
        515 testRunner['logTestFailure'] = oldLogTestFailure;
        516 }
        517};
        518
        519
        520/**
        521 * The return value of the equality predicate passed to findDifferences below,
        522 * in cases where the predicate can't test the input variables for equality.
        523 * @type {?string}
        524 */
        525goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS = null;
        526
        527
        528/**
        529 * The return value of the equality predicate passed to findDifferences below,
        530 * in cases where the input vriables are equal.
        531 * @type {?string}
        532 */
        533goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL = '';
        534
        535
        536/**
        537 * Determines if two items of any type match, and formulates an error message
        538 * if not.
        539 * @param {*} expected Expected argument to match.
        540 * @param {*} actual Argument as a result of performing the test.
        541 * @param {(function(string, *, *): ?string)=} opt_equalityPredicate An optional
        542 * function that can be used to check equality of variables. It accepts 3
        543 * arguments: type-of-variables, var1, var2 (in that order) and returns an
        544 * error message if the variables are not equal,
        545 * goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL if the variables
        546 * are equal, or
        547 * goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS if the predicate
        548 * couldn't check the input variables. The function will be called only if
        549 * the types of var1 and var2 are identical.
        550 * @return {?string} Null on success, error message on failure.
        551 */
        552goog.testing.asserts.findDifferences = function(expected, actual,
        553 opt_equalityPredicate) {
        554 var failures = [];
        555 var seen1 = [];
        556 var seen2 = [];
        557
        558 // To avoid infinite recursion when the two parameters are self-referential
        559 // along the same path of properties, keep track of the object pairs already
        560 // seen in this call subtree, and abort when a cycle is detected.
        561 function innerAssertWithCycleCheck(var1, var2, path) {
        562 // This is used for testing, so we can afford to be slow (but more
        563 // accurate). So we just check whether var1 is in seen1. If we
        564 // found var1 in index i, we simply need to check whether var2 is
        565 // in seen2[i]. If it is, we do not recurse to check var1/var2. If
        566 // it isn't, we know that the structures of the two objects must be
        567 // different.
        568 //
        569 // This is based on the fact that values at index i in seen1 and
        570 // seen2 will be checked for equality eventually (when
        571 // innerAssertImplementation(seen1[i], seen2[i], path) finishes).
        572 for (var i = 0; i < seen1.length; ++i) {
        573 var match1 = seen1[i] === var1;
        574 var match2 = seen2[i] === var2;
        575 if (match1 || match2) {
        576 if (!match1 || !match2) {
        577 // Asymmetric cycles, so the objects have different structure.
        578 failures.push('Asymmetric cycle detected at ' + path);
        579 }
        580 return;
        581 }
        582 }
        583
        584 seen1.push(var1);
        585 seen2.push(var2);
        586 innerAssertImplementation(var1, var2, path);
        587 seen1.pop();
        588 seen2.pop();
        589 }
        590
        591 var equalityPredicate = opt_equalityPredicate || function(type, var1, var2) {
        592 var typedPredicate = PRIMITIVE_EQUALITY_PREDICATES[type];
        593 if (!typedPredicate) {
        594 return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
        595 }
        596 var equal = typedPredicate(var1, var2);
        597 return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
        598 goog.testing.asserts.getDefaultErrorMsg_(var1, var2);
        599 };
        600
        601 /**
        602 * @param {*} var1 An item in the expected object.
        603 * @param {*} var2 The corresponding item in the actual object.
        604 * @param {string} path Their path in the objects.
        605 * @suppress {missingProperties} The map_ property is unknown to the compiler
        606 * unless goog.structs.Map is loaded.
        607 */
        608 function innerAssertImplementation(var1, var2, path) {
        609 if (var1 === var2) {
        610 return;
        611 }
        612
        613 var typeOfVar1 = _trueTypeOf(var1);
        614 var typeOfVar2 = _trueTypeOf(var2);
        615
        616 if (typeOfVar1 == typeOfVar2) {
        617 var isArray = typeOfVar1 == 'Array';
        618 var errorMessage = equalityPredicate(typeOfVar1, var1, var2);
        619 if (errorMessage !=
        620 goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS) {
        621 if (errorMessage !=
        622 goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL) {
        623 failures.push(path + ': ' + errorMessage);
        624 }
        625 } else if (isArray && var1.length != var2.length) {
        626 failures.push(path + ': Expected ' + var1.length + '-element array ' +
        627 'but got a ' + var2.length + '-element array');
        628 } else {
        629 var childPath = path + (isArray ? '[%s]' : (path ? '.%s' : '%s'));
        630
        631 // if an object has an __iterator__ property, we have no way of
        632 // actually inspecting its raw properties, and JS 1.7 doesn't
        633 // overload [] to make it possible for someone to generically
        634 // use what the iterator returns to compare the object-managed
        635 // properties. This gets us into deep poo with things like
        636 // goog.structs.Map, at least on systems that support iteration.
        637 if (!var1['__iterator__']) {
        638 for (var prop in var1) {
        639 if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
        640 // Skip array indices for now. We'll handle them later.
        641 continue;
        642 }
        643
        644 if (prop in var2) {
        645 innerAssertWithCycleCheck(var1[prop], var2[prop],
        646 childPath.replace('%s', prop));
        647 } else {
        648 failures.push('property ' + prop +
        649 ' not present in actual ' + (path || typeOfVar2));
        650 }
        651 }
        652 // make sure there aren't properties in var2 that are missing
        653 // from var1. if there are, then by definition they don't
        654 // match.
        655 for (var prop in var2) {
        656 if (isArray && goog.testing.asserts.isArrayIndexProp_(prop)) {
        657 // Skip array indices for now. We'll handle them later.
        658 continue;
        659 }
        660
        661 if (!(prop in var1)) {
        662 failures.push('property ' + prop +
        663 ' not present in expected ' +
        664 (path || typeOfVar1));
        665 }
        666 }
        667
        668 // Handle array indices by iterating from 0 to arr.length.
        669 //
        670 // Although all browsers allow holes in arrays, browsers
        671 // are inconsistent in what they consider a hole. For example,
        672 // "[0,undefined,2]" has a hole on IE but not on Firefox.
        673 //
        674 // Because our style guide bans for...in iteration over arrays,
        675 // we assume that most users don't care about holes in arrays,
        676 // and that it is ok to say that a hole is equivalent to a slot
        677 // populated with 'undefined'.
        678 if (isArray) {
        679 for (prop = 0; prop < var1.length; prop++) {
        680 innerAssertWithCycleCheck(var1[prop], var2[prop],
        681 childPath.replace('%s', String(prop)));
        682 }
        683 }
        684 } else {
        685 // special-case for closure objects that have iterators
        686 if (goog.isFunction(var1.equals)) {
        687 // use the object's own equals function, assuming it accepts an
        688 // object and returns a boolean
        689 if (!var1.equals(var2)) {
        690 failures.push('equals() returned false for ' +
        691 (path || typeOfVar1));
        692 }
        693 } else if (var1.map_) {
        694 // assume goog.structs.Map or goog.structs.Set, where comparing
        695 // their private map_ field is sufficient
        696 innerAssertWithCycleCheck(var1.map_, var2.map_,
        697 childPath.replace('%s', 'map_'));
        698 } else {
        699 // else die, so user knows we can't do anything
        700 failures.push('unable to check ' + (path || typeOfVar1) +
        701 ' for equality: it has an iterator we do not ' +
        702 'know how to handle. please add an equals method');
        703 }
        704 }
        705 }
        706 } else {
        707 failures.push(path + ' ' +
        708 goog.testing.asserts.getDefaultErrorMsg_(var1, var2));
        709 }
        710 }
        711
        712 innerAssertWithCycleCheck(expected, actual, '');
        713 return failures.length == 0 ? null :
        714 goog.testing.asserts.getDefaultErrorMsg_(expected, actual) +
        715 '\n ' + failures.join('\n ');
        716};
        717
        718
        719/**
        720 * Notes:
        721 * Object equality has some nasty browser quirks, and this implementation is
        722 * not 100% correct. For example,
        723 *
        724 * <code>
        725 * var a = [0, 1, 2];
        726 * var b = [0, 1, 2];
        727 * delete a[1];
        728 * b[1] = undefined;
        729 * assertObjectEquals(a, b); // should fail, but currently passes
        730 * </code>
        731 *
        732 * See asserts_test.html for more interesting edge cases.
        733 *
        734 * The first comparison object provided is the expected value, the second is
        735 * the actual.
        736 *
        737 * @param {*} a Assertion message or comparison object.
        738 * @param {*} b Comparison object.
        739 * @param {*=} opt_c Comparison object, if an assertion message was provided.
        740 */
        741var assertObjectEquals = function(a, b, opt_c) {
        742 _validateArguments(2, arguments);
        743 var v1 = nonCommentArg(1, 2, arguments);
        744 var v2 = nonCommentArg(2, 2, arguments);
        745 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
        746 var differences = goog.testing.asserts.findDifferences(v1, v2);
        747
        748 _assert(failureMessage, !differences, differences);
        749};
        750
        751
        752/**
        753 * Similar to assertObjectEquals above, but accepts a tolerance margin.
        754 *
        755 * @param {*} a Assertion message or comparison object.
        756 * @param {*} b Comparison object.
        757 * @param {*} c Comparison object or tolerance.
        758 * @param {*=} opt_d Tolerance, if an assertion message was provided.
        759 */
        760var assertObjectRoughlyEquals = function(a, b, c, opt_d) {
        761 _validateArguments(3, arguments);
        762 var v1 = nonCommentArg(1, 3, arguments);
        763 var v2 = nonCommentArg(2, 3, arguments);
        764 var tolerance = nonCommentArg(3, 3, arguments);
        765 var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
        766 var equalityPredicate = function(type, var1, var2) {
        767 var typedPredicate =
        768 goog.testing.asserts.primitiveRoughEqualityPredicates_[type];
        769 if (!typedPredicate) {
        770 return goog.testing.asserts.EQUALITY_PREDICATE_CANT_PROCESS;
        771 }
        772 var equal = typedPredicate(var1, var2, tolerance);
        773 return equal ? goog.testing.asserts.EQUALITY_PREDICATE_VARS_ARE_EQUAL :
        774 goog.testing.asserts.getDefaultErrorMsg_(var1, var2) +
        775 ' which was more than ' + tolerance + ' away';
        776 };
        777 var differences = goog.testing.asserts.findDifferences(
        778 v1, v2, equalityPredicate);
        779
        780 _assert(failureMessage, !differences, differences);
        781};
        782
        783
        784/**
        785 * Compares two arbitrary objects for non-equalness.
        786 *
        787 * All the same caveats as for assertObjectEquals apply here:
        788 * Undefined values may be confused for missing values, or vice versa.
        789 *
        790 * @param {*} a Assertion message or comparison object.
        791 * @param {*} b Comparison object.
        792 * @param {*=} opt_c Comparison object, if an assertion message was provided.
        793 */
        794var assertObjectNotEquals = function(a, b, opt_c) {
        795 _validateArguments(2, arguments);
        796 var v1 = nonCommentArg(1, 2, arguments);
        797 var v2 = nonCommentArg(2, 2, arguments);
        798 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
        799 var differences = goog.testing.asserts.findDifferences(v1, v2);
        800
        801 _assert(failureMessage, differences, 'Objects should not be equal');
        802};
        803
        804
        805/**
        806 * Compares two arrays ignoring negative indexes and extra properties on the
        807 * array objects. Use case: Internet Explorer adds the index, lastIndex and
        808 * input enumerable fields to the result of string.match(/regexp/g), which makes
        809 * assertObjectEquals fail.
        810 * @param {*} a The expected array (2 args) or the debug message (3 args).
        811 * @param {*} b The actual array (2 args) or the expected array (3 args).
        812 * @param {*=} opt_c The actual array (3 args only).
        813 */
        814var assertArrayEquals = function(a, b, opt_c) {
        815 _validateArguments(2, arguments);
        816 var v1 = nonCommentArg(1, 2, arguments);
        817 var v2 = nonCommentArg(2, 2, arguments);
        818 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
        819
        820 var typeOfVar1 = _trueTypeOf(v1);
        821 _assert(failureMessage,
        822 typeOfVar1 == 'Array',
        823 'Expected an array for assertArrayEquals but found a ' + typeOfVar1);
        824
        825 var typeOfVar2 = _trueTypeOf(v2);
        826 _assert(failureMessage,
        827 typeOfVar2 == 'Array',
        828 'Expected an array for assertArrayEquals but found a ' + typeOfVar2);
        829
        830 assertObjectEquals(failureMessage,
        831 Array.prototype.concat.call(v1), Array.prototype.concat.call(v2));
        832};
        833
        834
        835/**
        836 * Compares two objects that can be accessed like an array and assert that
        837 * each element is equal.
        838 * @param {string|Object} a Failure message (3 arguments)
        839 * or object #1 (2 arguments).
        840 * @param {Object} b Object #1 (2 arguments) or object #2 (3 arguments).
        841 * @param {Object=} opt_c Object #2 (3 arguments).
        842 */
        843var assertElementsEquals = function(a, b, opt_c) {
        844 _validateArguments(2, arguments);
        845
        846 var v1 = nonCommentArg(1, 2, arguments);
        847 var v2 = nonCommentArg(2, 2, arguments);
        848 var failureMessage = commentArg(2, arguments) ? commentArg(2, arguments) : '';
        849
        850 if (!v1) {
        851 assert(failureMessage, !v2);
        852 } else {
        853 assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
        854 for (var i = 0; i < v1.length; ++i) {
        855 assertEquals(
        856 'mismatch at index ' + i + ': ' + failureMessage, v1[i], v2[i]);
        857 }
        858 }
        859};
        860
        861
        862/**
        863 * Compares two objects that can be accessed like an array and assert that
        864 * each element is roughly equal.
        865 * @param {string|Object} a Failure message (4 arguments)
        866 * or object #1 (3 arguments).
        867 * @param {Object} b Object #1 (4 arguments) or object #2 (3 arguments).
        868 * @param {Object|number} c Object #2 (4 arguments) or tolerance (3 arguments).
        869 * @param {number=} opt_d tolerance (4 arguments).
        870 */
        871var assertElementsRoughlyEqual = function(a, b, c, opt_d) {
        872 _validateArguments(3, arguments);
        873
        874 var v1 = nonCommentArg(1, 3, arguments);
        875 var v2 = nonCommentArg(2, 3, arguments);
        876 var tolerance = nonCommentArg(3, 3, arguments);
        877 var failureMessage = commentArg(3, arguments) ? commentArg(3, arguments) : '';
        878
        879 if (!v1) {
        880 assert(failureMessage, !v2);
        881 } else {
        882 assertEquals('length mismatch: ' + failureMessage, v1.length, v2.length);
        883 for (var i = 0; i < v1.length; ++i) {
        884 assertRoughlyEquals(failureMessage, v1[i], v2[i], tolerance);
        885 }
        886 }
        887};
        888
        889
        890/**
        891 * Compares two array-like objects without taking their order into account.
        892 * @param {string|goog.testing.asserts.ArrayLike} a Assertion message or the
        893 * expected elements.
        894 * @param {goog.testing.asserts.ArrayLike} b Expected elements or the actual
        895 * elements.
        896 * @param {goog.testing.asserts.ArrayLike=} opt_c Actual elements.
        897 */
        898var assertSameElements = function(a, b, opt_c) {
        899 _validateArguments(2, arguments);
        900 var expected = nonCommentArg(1, 2, arguments);
        901 var actual = nonCommentArg(2, 2, arguments);
        902 var message = commentArg(2, arguments);
        903
        904 assertTrue('Bad arguments to assertSameElements(opt_message, expected: ' +
        905 'ArrayLike, actual: ArrayLike)',
        906 goog.isArrayLike(expected) && goog.isArrayLike(actual));
        907
        908 // Clones expected and actual and converts them to real arrays.
        909 expected = goog.testing.asserts.toArray_(expected);
        910 actual = goog.testing.asserts.toArray_(actual);
        911 // TODO(user): It would be great to show only the difference
        912 // between the expected and actual elements.
        913 _assert(message, expected.length == actual.length,
        914 'Expected ' + expected.length + ' elements: [' + expected + '], ' +
        915 'got ' + actual.length + ' elements: [' + actual + ']');
        916
        917 var toFind = goog.testing.asserts.toArray_(expected);
        918 for (var i = 0; i < actual.length; i++) {
        919 var index = goog.testing.asserts.indexOf_(toFind, actual[i]);
        920 _assert(message, index != -1, 'Expected [' + expected + '], got [' +
        921 actual + ']');
        922 toFind.splice(index, 1);
        923 }
        924};
        925
        926
        927/**
        928 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        929 * @param {*=} opt_b The value to assert (2 args only).
        930 */
        931var assertEvaluatesToTrue = function(a, opt_b) {
        932 _validateArguments(1, arguments);
        933 var value = nonCommentArg(1, 1, arguments);
        934 if (!value) {
        935 _assert(commentArg(1, arguments), false, 'Expected to evaluate to true');
        936 }
        937};
        938
        939
        940/**
        941 * @param {*} a The value to assert (1 arg) or debug message (2 args).
        942 * @param {*=} opt_b The value to assert (2 args only).
        943 */
        944var assertEvaluatesToFalse = function(a, opt_b) {
        945 _validateArguments(1, arguments);
        946 var value = nonCommentArg(1, 1, arguments);
        947 if (value) {
        948 _assert(commentArg(1, arguments), false, 'Expected to evaluate to false');
        949 }
        950};
        951
        952
        953/**
        954 * Compares two HTML snippets.
        955 *
        956 * Take extra care if attributes are involved. {@code assertHTMLEquals}'s
        957 * implementation isn't prepared for complex cases. For example, the following
        958 * comparisons erroneously fail:
        959 * <pre>
        960 * assertHTMLEquals('<a href="x" target="y">', '<a target="y" href="x">');
        961 * assertHTMLEquals('<div class="a b">', '<div class="b a">');
        962 * assertHTMLEquals('<input disabled>', '<input disabled="disabled">');
        963 * </pre>
        964 *
        965 * When in doubt, use {@code goog.testing.dom.assertHtmlMatches}.
        966 *
        967 * @param {*} a The expected value (2 args) or the debug message (3 args).
        968 * @param {*} b The actual value (2 args) or the expected value (3 args).
        969 * @param {*=} opt_c The actual value (3 args only).
        970 */
        971var assertHTMLEquals = function(a, b, opt_c) {
        972 _validateArguments(2, arguments);
        973 var var1 = nonCommentArg(1, 2, arguments);
        974 var var2 = nonCommentArg(2, 2, arguments);
        975 var var1Standardized = standardizeHTML(var1);
        976 var var2Standardized = standardizeHTML(var2);
        977
        978 _assert(commentArg(2, arguments), var1Standardized === var2Standardized,
        979 goog.testing.asserts.getDefaultErrorMsg_(
        980 var1Standardized, var2Standardized));
        981};
        982
        983
        984/**
        985 * Compares two CSS property values to make sure that they represent the same
        986 * things. This will normalize values in the browser. For example, in Firefox,
        987 * this assertion will consider "rgb(0, 0, 255)" and "#0000ff" to be identical
        988 * values for the "color" property. This function won't normalize everything --
        989 * for example, in most browsers, "blue" will not match "#0000ff". It is
        990 * intended only to compensate for unexpected normalizations performed by
        991 * the browser that should also affect your expected value.
        992 * @param {string} a Assertion message, or the CSS property name.
        993 * @param {string} b CSS property name, or the expected value.
        994 * @param {string} c The expected value, or the actual value.
        995 * @param {string=} opt_d The actual value.
        996 */
        997var assertCSSValueEquals = function(a, b, c, opt_d) {
        998 _validateArguments(3, arguments);
        999 var propertyName = nonCommentArg(1, 3, arguments);
        1000 var expectedValue = nonCommentArg(2, 3, arguments);
        1001 var actualValue = nonCommentArg(3, 3, arguments);
        1002 var expectedValueStandardized =
        1003 standardizeCSSValue(propertyName, expectedValue);
        1004 var actualValueStandardized =
        1005 standardizeCSSValue(propertyName, actualValue);
        1006
        1007 _assert(commentArg(3, arguments),
        1008 expectedValueStandardized == actualValueStandardized,
        1009 goog.testing.asserts.getDefaultErrorMsg_(
        1010 expectedValueStandardized, actualValueStandardized));
        1011};
        1012
        1013
        1014/**
        1015 * @param {*} a The expected value (2 args) or the debug message (3 args).
        1016 * @param {*} b The actual value (2 args) or the expected value (3 args).
        1017 * @param {*=} opt_c The actual value (3 args only).
        1018 */
        1019var assertHashEquals = function(a, b, opt_c) {
        1020 _validateArguments(2, arguments);
        1021 var var1 = nonCommentArg(1, 2, arguments);
        1022 var var2 = nonCommentArg(2, 2, arguments);
        1023 var message = commentArg(2, arguments);
        1024 for (var key in var1) {
        1025 _assert(message,
        1026 key in var2, 'Expected hash had key ' + key + ' that was not found');
        1027 _assert(message, var1[key] == var2[key], 'Value for key ' + key +
        1028 ' mismatch - expected = ' + var1[key] + ', actual = ' + var2[key]);
        1029 }
        1030
        1031 for (var key in var2) {
        1032 _assert(message, key in var1, 'Actual hash had key ' + key +
        1033 ' that was not expected');
        1034 }
        1035};
        1036
        1037
        1038/**
        1039 * @param {*} a The expected value (3 args) or the debug message (4 args).
        1040 * @param {*} b The actual value (3 args) or the expected value (4 args).
        1041 * @param {*} c The tolerance (3 args) or the actual value (4 args).
        1042 * @param {*=} opt_d The tolerance (4 args only).
        1043 */
        1044var assertRoughlyEquals = function(a, b, c, opt_d) {
        1045 _validateArguments(3, arguments);
        1046 var expected = nonCommentArg(1, 3, arguments);
        1047 var actual = nonCommentArg(2, 3, arguments);
        1048 var tolerance = nonCommentArg(3, 3, arguments);
        1049 _assert(commentArg(3, arguments),
        1050 goog.testing.asserts.numberRoughEqualityPredicate_(
        1051 expected, actual, tolerance),
        1052 'Expected ' + expected + ', but got ' + actual +
        1053 ' which was more than ' + tolerance + ' away');
        1054};
        1055
        1056
        1057/**
        1058 * Checks if the test value is a member of the given container. Uses
        1059 * container.indexOf as the underlying function, so this works for strings
        1060 * and arrays.
        1061 * @param {*} a Failure message (3 arguments) or the test value
        1062 * (2 arguments).
        1063 * @param {*} b The test value (3 arguments) or the container
        1064 * (2 arguments).
        1065 * @param {*=} opt_c The container.
        1066 */
        1067var assertContains = function(a, b, opt_c) {
        1068 _validateArguments(2, arguments);
        1069 var contained = nonCommentArg(1, 2, arguments);
        1070 var container = nonCommentArg(2, 2, arguments);
        1071 _assert(commentArg(2, arguments),
        1072 goog.testing.asserts.contains_(container, contained),
        1073 'Expected \'' + container + '\' to contain \'' + contained + '\'');
        1074};
        1075
        1076
        1077/**
        1078 * Checks if the given element is not the member of the given container.
        1079 * @param {*} a Failure message (3 arguments) or the contained element
        1080 * (2 arguments).
        1081 * @param {*} b The contained element (3 arguments) or the container
        1082 * (2 arguments).
        1083 * @param {*=} opt_c The container.
        1084 */
        1085var assertNotContains = function(a, b, opt_c) {
        1086 _validateArguments(2, arguments);
        1087 var contained = nonCommentArg(1, 2, arguments);
        1088 var container = nonCommentArg(2, 2, arguments);
        1089 _assert(commentArg(2, arguments),
        1090 !goog.testing.asserts.contains_(container, contained),
        1091 'Expected \'' + container + '\' not to contain \'' + contained + '\'');
        1092};
        1093
        1094
        1095/**
        1096 * Checks if the given string matches the given regular expression.
        1097 * @param {*} a Failure message (3 arguments) or the expected regular
        1098 * expression as a string or RegExp (2 arguments).
        1099 * @param {*} b The regular expression (3 arguments) or the string to test
        1100 * (2 arguments).
        1101 * @param {*=} opt_c The string to test.
        1102 */
        1103var assertRegExp = function(a, b, opt_c) {
        1104 _validateArguments(2, arguments);
        1105 var regexp = nonCommentArg(1, 2, arguments);
        1106 var string = nonCommentArg(2, 2, arguments);
        1107 if (typeof(regexp) == 'string') {
        1108 regexp = new RegExp(regexp);
        1109 }
        1110 _assert(commentArg(2, arguments),
        1111 regexp.test(string),
        1112 'Expected \'' + string + '\' to match RegExp ' + regexp.toString());
        1113};
        1114
        1115
        1116/**
        1117 * Converts an array like object to array or clones it if it's already array.
        1118 * @param {goog.testing.asserts.ArrayLike} arrayLike The collection.
        1119 * @return {!Array<?>} Copy of the collection as array.
        1120 * @private
        1121 */
        1122goog.testing.asserts.toArray_ = function(arrayLike) {
        1123 var ret = [];
        1124 for (var i = 0; i < arrayLike.length; i++) {
        1125 ret[i] = arrayLike[i];
        1126 }
        1127 return ret;
        1128};
        1129
        1130
        1131/**
        1132 * Finds the position of the first occurrence of an element in a container.
        1133 * @param {goog.testing.asserts.ArrayLike} container
        1134 * The array to find the element in.
        1135 * @param {*} contained Element to find.
        1136 * @return {number} Index of the first occurrence or -1 if not found.
        1137 * @private
        1138 */
        1139goog.testing.asserts.indexOf_ = function(container, contained) {
        1140 if (container.indexOf) {
        1141 return container.indexOf(contained);
        1142 } else {
        1143 // IE6/7 do not have indexOf so do a search.
        1144 for (var i = 0; i < container.length; i++) {
        1145 if (container[i] === contained) {
        1146 return i;
        1147 }
        1148 }
        1149 return -1;
        1150 }
        1151};
        1152
        1153
        1154/**
        1155 * Tells whether the array contains the given element.
        1156 * @param {goog.testing.asserts.ArrayLike} container The array to
        1157 * find the element in.
        1158 * @param {*} contained Element to find.
        1159 * @return {boolean} Whether the element is in the array.
        1160 * @private
        1161 */
        1162goog.testing.asserts.contains_ = function(container, contained) {
        1163 // TODO(user): Can we check for container.contains as well?
        1164 // That would give us support for most goog.structs (though weird results
        1165 // with anything else with a contains method, like goog.math.Range). Falling
        1166 // back with container.some would catch all iterables, too.
        1167 return goog.testing.asserts.indexOf_(container, contained) != -1;
        1168};
        1169
        1170var standardizeHTML = function(html) {
        1171 var translator = document.createElement('DIV');
        1172 translator.innerHTML = html;
        1173
        1174 // Trim whitespace from result (without relying on goog.string)
        1175 return translator.innerHTML.replace(/^\s+|\s+$/g, '');
        1176};
        1177
        1178
        1179/**
        1180 * Standardizes a CSS value for a given property by applying it to an element
        1181 * and then reading it back.
        1182 * @param {string} propertyName CSS property name.
        1183 * @param {string} value CSS value.
        1184 * @return {string} Normalized CSS value.
        1185 */
        1186var standardizeCSSValue = function(propertyName, value) {
        1187 var styleDeclaration = document.createElement('DIV').style;
        1188 styleDeclaration[propertyName] = value;
        1189 return styleDeclaration[propertyName];
        1190};
        1191
        1192
        1193/**
        1194 * Raises a JsUnit exception with the given comment.
        1195 * @param {string} comment A summary for the exception.
        1196 * @param {string=} opt_message A description of the exception.
        1197 */
        1198goog.testing.asserts.raiseException = function(comment, opt_message) {
        1199 throw new goog.testing.JsUnitException(comment, opt_message);
        1200};
        1201
        1202
        1203/**
        1204 * Helper function for assertObjectEquals.
        1205 * @param {string} prop A property name.
        1206 * @return {boolean} If the property name is an array index.
        1207 * @private
        1208 */
        1209goog.testing.asserts.isArrayIndexProp_ = function(prop) {
        1210 return (prop | 0) == prop;
        1211};
        1212
        1213
        1214
        1215/**
        1216 * @param {string} comment A summary for the exception.
        1217 * @param {?string=} opt_message A description of the exception.
        1218 * @constructor
        1219 * @extends {Error}
        1220 * @final
        1221 */
        1222goog.testing.JsUnitException = function(comment, opt_message) {
        1223 this.isJsUnitException = true;
        1224 this.message = (comment ? comment : '') +
        1225 (comment && opt_message ? '\n' : '') +
        1226 (opt_message ? opt_message : '');
        1227 this.stackTrace = goog.testing.stacktrace.get();
        1228 // These fields are for compatibility with jsUnitTestManager.
        1229 this.comment = comment || null;
        1230 this.jsUnitMessage = opt_message || '';
        1231
        1232 // Ensure there is a stack trace.
        1233 if (Error.captureStackTrace) {
        1234 Error.captureStackTrace(this, goog.testing.JsUnitException);
        1235 } else {
        1236 this.stack = new Error().stack || '';
        1237 }
        1238};
        1239goog.inherits(goog.testing.JsUnitException, Error);
        1240
        1241
        1242/** @override */
        1243goog.testing.JsUnitException.prototype.toString = function() {
        1244 return this.message;
        1245};
        1246
        1247
        1248goog.exportSymbol('fail', fail);
        1249goog.exportSymbol('assert', assert);
        1250goog.exportSymbol('assertThrows', assertThrows);
        1251goog.exportSymbol('assertNotThrows', assertNotThrows);
        1252goog.exportSymbol('assertTrue', assertTrue);
        1253goog.exportSymbol('assertFalse', assertFalse);
        1254goog.exportSymbol('assertEquals', assertEquals);
        1255goog.exportSymbol('assertNotEquals', assertNotEquals);
        1256goog.exportSymbol('assertNull', assertNull);
        1257goog.exportSymbol('assertNotNull', assertNotNull);
        1258goog.exportSymbol('assertUndefined', assertUndefined);
        1259goog.exportSymbol('assertNotUndefined', assertNotUndefined);
        1260goog.exportSymbol('assertNotNullNorUndefined', assertNotNullNorUndefined);
        1261goog.exportSymbol('assertNonEmptyString', assertNonEmptyString);
        1262goog.exportSymbol('assertNaN', assertNaN);
        1263goog.exportSymbol('assertNotNaN', assertNotNaN);
        1264goog.exportSymbol('assertObjectEquals', assertObjectEquals);
        1265goog.exportSymbol('assertObjectRoughlyEquals', assertObjectRoughlyEquals);
        1266goog.exportSymbol('assertObjectNotEquals', assertObjectNotEquals);
        1267goog.exportSymbol('assertArrayEquals', assertArrayEquals);
        1268goog.exportSymbol('assertElementsEquals', assertElementsEquals);
        1269goog.exportSymbol('assertElementsRoughlyEqual', assertElementsRoughlyEqual);
        1270goog.exportSymbol('assertSameElements', assertSameElements);
        1271goog.exportSymbol('assertEvaluatesToTrue', assertEvaluatesToTrue);
        1272goog.exportSymbol('assertEvaluatesToFalse', assertEvaluatesToFalse);
        1273goog.exportSymbol('assertHTMLEquals', assertHTMLEquals);
        1274goog.exportSymbol('assertHashEquals', assertHashEquals);
        1275goog.exportSymbol('assertRoughlyEquals', assertRoughlyEquals);
        1276goog.exportSymbol('assertContains', assertContains);
        1277goog.exportSymbol('assertNotContains', assertNotContains);
        1278goog.exportSymbol('assertRegExp', assertRegExp);
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/asynctestcase.js.src.html b/docs/source/lib/goog/testing/asynctestcase.js.src.html new file mode 100644 index 0000000..0c164e4 --- /dev/null +++ b/docs/source/lib/goog/testing/asynctestcase.js.src.html @@ -0,0 +1 @@ +asynctestcase.js

        lib/goog/testing/asynctestcase.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// All Rights Reserved.
        15
        16/**
        17 * @fileoverview A class representing a set of test functions that use
        18 * asynchronous functions that cannot be meaningfully mocked.
        19 *
        20 * To create a Google-compatable JsUnit test using this test case, put the
        21 * following snippet in your test:
        22 *
        23 * var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
        24 *
        25 * To make the test runner wait for your asynchronous behaviour, use:
        26 *
        27 * asyncTestCase.waitForAsync('Waiting for xhr to respond');
        28 *
        29 * The next test will not start until the following call is made, or a
        30 * timeout occurs:
        31 *
        32 * asyncTestCase.continueTesting();
        33 *
        34 * There does NOT need to be a 1:1 mapping of waitForAsync calls and
        35 * continueTesting calls. The next test will be run after a single call to
        36 * continueTesting is made, as long as there is no subsequent call to
        37 * waitForAsync in the same thread.
        38 *
        39 * Example:
        40 * // Returning here would cause the next test to be run.
        41 * asyncTestCase.waitForAsync('description 1');
        42 * // Returning here would *not* cause the next test to be run.
        43 * // Only effect of additional waitForAsync() calls is an updated
        44 * // description in the case of a timeout.
        45 * asyncTestCase.waitForAsync('updated description');
        46 * asyncTestCase.continueTesting();
        47 * // Returning here would cause the next test to be run.
        48 * asyncTestCase.waitForAsync('just kidding, still running.');
        49 * // Returning here would *not* cause the next test to be run.
        50 *
        51 * The test runner can also be made to wait for more than one asynchronous
        52 * event with:
        53 *
        54 * asyncTestCase.waitForSignals(n);
        55 *
        56 * The next test will not start until asyncTestCase.signal() is called n times,
        57 * or the test step timeout is exceeded.
        58 *
        59 * This class supports asynchronous behaviour in all test functions except for
        60 * tearDownPage. If such support is needed, it can be added.
        61 *
        62 * Example Usage:
        63 *
        64 * var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
        65 * // Optionally, set a longer-than-normal step timeout.
        66 * asyncTestCase.stepTimeout = 30 * 1000;
        67 *
        68 * function testSetTimeout() {
        69 * var step = 0;
        70 * function stepCallback() {
        71 * step++;
        72 * switch (step) {
        73 * case 1:
        74 * var startTime = goog.now();
        75 * asyncTestCase.waitForAsync('step 1');
        76 * window.setTimeout(stepCallback, 100);
        77 * break;
        78 * case 2:
        79 * assertTrue('Timeout fired too soon',
        80 * goog.now() - startTime >= 100);
        81 * asyncTestCase.waitForAsync('step 2');
        82 * window.setTimeout(stepCallback, 100);
        83 * break;
        84 * case 3:
        85 * assertTrue('Timeout fired too soon',
        86 * goog.now() - startTime >= 200);
        87 * asyncTestCase.continueTesting();
        88 * break;
        89 * default:
        90 * fail('Unexpected call to stepCallback');
        91 * }
        92 * }
        93 * stepCallback();
        94 * }
        95 *
        96 * Known Issues:
        97 * IE7 Exceptions:
        98 * As the failingtest.html will show, it appears as though ie7 does not
        99 * propagate an exception past a function called using the func.call()
        100 * syntax. This causes case 3 of the failing tests (exceptions) to show up
        101 * as timeouts in IE.
        102 * window.onerror:
        103 * This seems to catch errors only in ff2/ff3. It does not work in Safari or
        104 * IE7. The consequence of this is that exceptions that would have been
        105 * caught by window.onerror show up as timeouts.
        106 *
        107 * @author agrieve@google.com (Andrew Grieve)
        108 */
        109
        110goog.provide('goog.testing.AsyncTestCase');
        111goog.provide('goog.testing.AsyncTestCase.ControlBreakingException');
        112
        113goog.require('goog.testing.TestCase');
        114goog.require('goog.testing.TestCase.Test');
        115goog.require('goog.testing.asserts');
        116
        117
        118
        119/**
        120 * A test case that is capable of running tests the contain asynchronous logic.
        121 * @param {string=} opt_name A descriptive name for the test case.
        122 * @extends {goog.testing.TestCase}
        123 * @constructor
        124 */
        125goog.testing.AsyncTestCase = function(opt_name) {
        126 goog.testing.TestCase.call(this, opt_name);
        127};
        128goog.inherits(goog.testing.AsyncTestCase, goog.testing.TestCase);
        129
        130
        131/**
        132 * Represents result of top stack function call.
        133 * @typedef {{controlBreakingExceptionThrown: boolean, message: string}}
        134 * @private
        135 */
        136goog.testing.AsyncTestCase.TopStackFuncResult_;
        137
        138
        139
        140/**
        141 * An exception class used solely for control flow.
        142 * @param {string=} opt_message Error message.
        143 * @constructor
        144 * @final
        145 */
        146goog.testing.AsyncTestCase.ControlBreakingException = function(opt_message) {
        147 /**
        148 * The exception message.
        149 * @type {string}
        150 */
        151 this.message = opt_message || '';
        152};
        153
        154
        155/**
        156 * Return value for .toString().
        157 * @type {string}
        158 */
        159goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING =
        160 '[AsyncTestCase.ControlBreakingException]';
        161
        162
        163/**
        164 * Marks this object as a ControlBreakingException
        165 * @type {boolean}
        166 */
        167goog.testing.AsyncTestCase.ControlBreakingException.prototype.
        168 isControlBreakingException = true;
        169
        170
        171/** @override */
        172goog.testing.AsyncTestCase.ControlBreakingException.prototype.toString =
        173 function() {
        174 // This shows up in the console when the exception is not caught.
        175 return goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
        176};
        177
        178
        179/**
        180 * How long to wait for a single step of a test to complete in milliseconds.
        181 * A step starts when a call to waitForAsync() is made.
        182 * @type {number}
        183 */
        184goog.testing.AsyncTestCase.prototype.stepTimeout = 1000;
        185
        186
        187/**
        188 * How long to wait after a failed test before moving onto the next one.
        189 * The purpose of this is to allow any pending async callbacks from the failing
        190 * test to finish up and not cause the next test to fail.
        191 * @type {number}
        192 */
        193goog.testing.AsyncTestCase.prototype.timeToSleepAfterFailure = 500;
        194
        195
        196/**
        197 * Turn on extra logging to help debug failing async. tests.
        198 * @type {boolean}
        199 * @private
        200 */
        201goog.testing.AsyncTestCase.prototype.enableDebugLogs_ = false;
        202
        203
        204/**
        205 * A reference to the original asserts.js assert_() function.
        206 * @private
        207 */
        208goog.testing.AsyncTestCase.prototype.origAssert_;
        209
        210
        211/**
        212 * A reference to the original asserts.js fail() function.
        213 * @private
        214 */
        215goog.testing.AsyncTestCase.prototype.origFail_;
        216
        217
        218/**
        219 * A reference to the original window.onerror function.
        220 * @type {Function|undefined}
        221 * @private
        222 */
        223goog.testing.AsyncTestCase.prototype.origOnError_;
        224
        225
        226/**
        227 * The stage of the test we are currently on.
        228 * @type {Function|undefined}}
        229 * @private
        230 */
        231goog.testing.AsyncTestCase.prototype.curStepFunc_;
        232
        233
        234/**
        235 * The name of the stage of the test we are currently on.
        236 * @type {string}
        237 * @private
        238 */
        239goog.testing.AsyncTestCase.prototype.curStepName_ = '';
        240
        241
        242/**
        243 * The stage of the test we should run next.
        244 * @type {Function|undefined}
        245 * @private
        246 */
        247goog.testing.AsyncTestCase.prototype.nextStepFunc;
        248
        249
        250/**
        251 * The name of the stage of the test we should run next.
        252 * @type {string}
        253 * @private
        254 */
        255goog.testing.AsyncTestCase.prototype.nextStepName_ = '';
        256
        257
        258/**
        259 * The handle to the current setTimeout timer.
        260 * @type {number}
        261 * @private
        262 */
        263goog.testing.AsyncTestCase.prototype.timeoutHandle_ = 0;
        264
        265
        266/**
        267 * Marks if the cleanUp() function has been called for the currently running
        268 * test.
        269 * @type {boolean}
        270 * @private
        271 */
        272goog.testing.AsyncTestCase.prototype.cleanedUp_ = false;
        273
        274
        275/**
        276 * The currently active test.
        277 * @type {goog.testing.TestCase.Test|undefined}
        278 * @protected
        279 */
        280goog.testing.AsyncTestCase.prototype.activeTest;
        281
        282
        283/**
        284 * A flag to prevent recursive exception handling.
        285 * @type {boolean}
        286 * @private
        287 */
        288goog.testing.AsyncTestCase.prototype.inException_ = false;
        289
        290
        291/**
        292 * Flag used to determine if we can move to the next step in the testing loop.
        293 * @type {boolean}
        294 * @private
        295 */
        296goog.testing.AsyncTestCase.prototype.isReady_ = true;
        297
        298
        299/**
        300 * Number of signals to wait for before continuing testing when waitForSignals
        301 * is used.
        302 * @type {number}
        303 * @private
        304 */
        305goog.testing.AsyncTestCase.prototype.expectedSignalCount_ = 0;
        306
        307
        308/**
        309 * Number of signals received.
        310 * @type {number}
        311 * @private
        312 */
        313goog.testing.AsyncTestCase.prototype.receivedSignalCount_ = 0;
        314
        315
        316/**
        317 * Flag that tells us if there is a function in the call stack that will make
        318 * a call to pump_().
        319 * @type {boolean}
        320 * @private
        321 */
        322goog.testing.AsyncTestCase.prototype.returnWillPump_ = false;
        323
        324
        325/**
        326 * The number of times we have thrown a ControlBreakingException so that we
        327 * know not to complain in our window.onerror handler. In Webkit, window.onerror
        328 * is not supported, and so this counter will keep going up but we won't care
        329 * about it.
        330 * @type {number}
        331 * @private
        332 */
        333goog.testing.AsyncTestCase.prototype.numControlExceptionsExpected_ = 0;
        334
        335
        336/**
        337 * The current step name.
        338 * @return {!string} Step name.
        339 * @protected
        340 */
        341goog.testing.AsyncTestCase.prototype.getCurrentStepName = function() {
        342 return this.curStepName_;
        343};
        344
        345
        346/**
        347 * Preferred way of creating an AsyncTestCase. Creates one and initializes it
        348 * with the G_testRunner.
        349 * @param {string=} opt_name A descriptive name for the test case.
        350 * @return {!goog.testing.AsyncTestCase} The created AsyncTestCase.
        351 */
        352goog.testing.AsyncTestCase.createAndInstall = function(opt_name) {
        353 var asyncTestCase = new goog.testing.AsyncTestCase(opt_name);
        354 goog.testing.TestCase.initializeTestRunner(asyncTestCase);
        355 return asyncTestCase;
        356};
        357
        358
        359/**
        360 * Informs the testcase not to continue to the next step in the test cycle
        361 * until continueTesting is called.
        362 * @param {string=} opt_name A description of what we are waiting for.
        363 */
        364goog.testing.AsyncTestCase.prototype.waitForAsync = function(opt_name) {
        365 this.isReady_ = false;
        366 this.curStepName_ = opt_name || this.curStepName_;
        367
        368 // Reset the timer that tracks if the async test takes too long.
        369 this.stopTimeoutTimer_();
        370 this.startTimeoutTimer_();
        371};
        372
        373
        374/**
        375 * Continue with the next step in the test cycle.
        376 */
        377goog.testing.AsyncTestCase.prototype.continueTesting = function() {
        378 if (this.receivedSignalCount_ < this.expectedSignalCount_) {
        379 var remaining = this.expectedSignalCount_ - this.receivedSignalCount_;
        380 throw Error('Still waiting for ' + remaining + ' signals.');
        381 }
        382 this.endCurrentStep_();
        383};
        384
        385
        386/**
        387 * Ends the current test step and queues the next test step to run.
        388 * @private
        389 */
        390goog.testing.AsyncTestCase.prototype.endCurrentStep_ = function() {
        391 if (!this.isReady_) {
        392 // We are a potential entry point, so we pump.
        393 this.isReady_ = true;
        394 this.stopTimeoutTimer_();
        395 // Run this in a setTimeout so that the caller has a chance to call
        396 // waitForAsync() again before we continue.
        397 this.timeout(goog.bind(this.pump_, this, null), 0);
        398 }
        399};
        400
        401
        402/**
        403 * Informs the testcase not to continue to the next step in the test cycle
        404 * until signal is called the specified number of times. Within a test, this
        405 * function behaves additively if called multiple times; the number of signals
        406 * to wait for will be the sum of all expected number of signals this function
        407 * was called with.
        408 * @param {number} times The number of signals to receive before
        409 * continuing testing.
        410 * @param {string=} opt_name A description of what we are waiting for.
        411 */
        412goog.testing.AsyncTestCase.prototype.waitForSignals =
        413 function(times, opt_name) {
        414 this.expectedSignalCount_ += times;
        415 if (this.receivedSignalCount_ < this.expectedSignalCount_) {
        416 this.waitForAsync(opt_name);
        417 }
        418};
        419
        420
        421/**
        422 * Signals once to continue with the test. If this is the last signal that the
        423 * test was waiting on, call continueTesting.
        424 */
        425goog.testing.AsyncTestCase.prototype.signal = function() {
        426 if (++this.receivedSignalCount_ === this.expectedSignalCount_ &&
        427 this.expectedSignalCount_ > 0) {
        428 this.endCurrentStep_();
        429 }
        430};
        431
        432
        433/**
        434 * Handles an exception thrown by a test.
        435 * @param {*=} opt_e The exception object associated with the failure
        436 * or a string.
        437 * @throws Always throws a ControlBreakingException.
        438 */
        439goog.testing.AsyncTestCase.prototype.doAsyncError = function(opt_e) {
        440 // If we've caught an exception that we threw, then just pass it along. This
        441 // can happen if doAsyncError() was called from a call to assert and then
        442 // again by pump_().
        443 if (opt_e && opt_e.isControlBreakingException) {
        444 throw opt_e;
        445 }
        446
        447 // Prevent another timeout error from triggering for this test step.
        448 this.stopTimeoutTimer_();
        449
        450 // doError() uses test.name. Here, we create a dummy test and give it a more
        451 // helpful name based on the step we're currently on.
        452 var fakeTestObj = new goog.testing.TestCase.Test(this.curStepName_,
        453 goog.nullFunction);
        454 if (this.activeTest) {
        455 fakeTestObj.name = this.activeTest.name + ' [' + fakeTestObj.name + ']';
        456 }
        457
        458 if (this.activeTest) {
        459 // Note: if the test has an error, and then tearDown has an error, they will
        460 // both be reported.
        461 this.doError(fakeTestObj, opt_e);
        462 } else {
        463 this.exceptionBeforeTest = opt_e;
        464 }
        465
        466 // This is a potential entry point, so we pump. We also add in a bit of a
        467 // delay to try and prevent any async behavior from the failed test from
        468 // causing the next test to fail.
        469 this.timeout(goog.bind(this.pump_, this, this.doAsyncErrorTearDown_),
        470 this.timeToSleepAfterFailure);
        471
        472 // We just caught an exception, so we do not want the code above us on the
        473 // stack to continue executing. If pump_ is in our call-stack, then it will
        474 // batch together multiple errors, so we only increment the count if pump_ is
        475 // not in the stack and let pump_ increment the count when it batches them.
        476 if (!this.returnWillPump_) {
        477 this.numControlExceptionsExpected_ += 1;
        478 this.dbgLog_('doAsynError: numControlExceptionsExpected_ = ' +
        479 this.numControlExceptionsExpected_ + ' and throwing exception.');
        480 }
        481
        482 // Copy the error message to ControlBreakingException.
        483 var message = '';
        484 if (typeof opt_e == 'string') {
        485 message = opt_e;
        486 } else if (opt_e && opt_e.message) {
        487 message = opt_e.message;
        488 }
        489 throw new goog.testing.AsyncTestCase.ControlBreakingException(message);
        490};
        491
        492
        493/**
        494 * Sets up the test page and then waits until the test case has been marked
        495 * as ready before executing the tests.
        496 * @override
        497 */
        498goog.testing.AsyncTestCase.prototype.runTests = function() {
        499 this.hookAssert_();
        500 this.hookOnError_();
        501
        502 this.setNextStep_(this.doSetUpPage_, 'setUpPage');
        503 // We are an entry point, so we pump.
        504 this.pump_();
        505};
        506
        507
        508/**
        509 * Starts the tests.
        510 * @override
        511 */
        512goog.testing.AsyncTestCase.prototype.cycleTests = function() {
        513 // We are an entry point, so we pump.
        514 this.saveMessage('Start');
        515 this.setNextStep_(this.doIteration_, 'doIteration');
        516 this.pump_();
        517};
        518
        519
        520/**
        521 * Finalizes the test case, called when the tests have finished executing.
        522 * @override
        523 */
        524goog.testing.AsyncTestCase.prototype.finalize = function() {
        525 this.unhookAll_();
        526 this.setNextStep_(null, 'finalized');
        527 goog.testing.AsyncTestCase.superClass_.finalize.call(this);
        528};
        529
        530
        531/**
        532 * Enables verbose logging of what is happening inside of the AsyncTestCase.
        533 */
        534goog.testing.AsyncTestCase.prototype.enableDebugLogging = function() {
        535 this.enableDebugLogs_ = true;
        536};
        537
        538
        539/**
        540 * Logs the given debug message to the console (when enabled).
        541 * @param {string} message The message to log.
        542 * @private
        543 */
        544goog.testing.AsyncTestCase.prototype.dbgLog_ = function(message) {
        545 if (this.enableDebugLogs_) {
        546 this.log('AsyncTestCase - ' + message);
        547 }
        548};
        549
        550
        551/**
        552 * Wraps doAsyncError() for when we are sure that the test runner has no user
        553 * code above it in the stack.
        554 * @param {string|Error=} opt_e The exception object associated with the
        555 * failure or a string.
        556 * @private
        557 */
        558goog.testing.AsyncTestCase.prototype.doTopOfStackAsyncError_ =
        559 function(opt_e) {
        560 /** @preserveTry */
        561 try {
        562 this.doAsyncError(opt_e);
        563 } catch (e) {
        564 // We know that we are on the top of the stack, so there is no need to
        565 // throw this exception in this case.
        566 if (e.isControlBreakingException) {
        567 this.numControlExceptionsExpected_ -= 1;
        568 this.dbgLog_('doTopOfStackAsyncError_: numControlExceptionsExpected_ = ' +
        569 this.numControlExceptionsExpected_ + ' and catching exception.');
        570 } else {
        571 throw e;
        572 }
        573 }
        574};
        575
        576
        577/**
        578 * Calls the tearDown function, catching any errors, and then moves on to
        579 * the next step in the testing cycle.
        580 * @private
        581 */
        582goog.testing.AsyncTestCase.prototype.doAsyncErrorTearDown_ = function() {
        583 if (this.inException_) {
        584 // We get here if tearDown is throwing the error.
        585 // Upon calling continueTesting, the inline function 'doAsyncError' (set
        586 // below) is run.
        587 this.endCurrentStep_();
        588 } else {
        589 this.inException_ = true;
        590 this.isReady_ = true;
        591
        592 // The continue point is different depending on if the error happened in
        593 // setUpPage() or in setUp()/test*()/tearDown().
        594 var stepFuncAfterError = this.nextStepFunc_;
        595 var stepNameAfterError = 'TestCase.execute (after error)';
        596 if (this.activeTest) {
        597 stepFuncAfterError = this.doIteration_;
        598 stepNameAfterError = 'doIteration (after error)';
        599 }
        600
        601 // We must set the next step before calling tearDown.
        602 this.setNextStep_(function() {
        603 this.inException_ = false;
        604 // This is null when an error happens in setUpPage.
        605 this.setNextStep_(stepFuncAfterError, stepNameAfterError);
        606 }, 'doAsyncError');
        607
        608 // Call the test's tearDown().
        609 if (!this.cleanedUp_) {
        610 this.cleanedUp_ = true;
        611 this.tearDown();
        612 }
        613 }
        614};
        615
        616
        617/**
        618 * Replaces the asserts.js assert_() and fail() functions with a wrappers to
        619 * catch the exceptions.
        620 * @private
        621 */
        622goog.testing.AsyncTestCase.prototype.hookAssert_ = function() {
        623 if (!this.origAssert_) {
        624 this.origAssert_ = _assert;
        625 this.origFail_ = fail;
        626 var self = this;
        627 _assert = function() {
        628 /** @preserveTry */
        629 try {
        630 self.origAssert_.apply(this, arguments);
        631 } catch (e) {
        632 self.dbgLog_('Wrapping failed assert()');
        633 self.doAsyncError(e);
        634 }
        635 };
        636 fail = function() {
        637 /** @preserveTry */
        638 try {
        639 self.origFail_.apply(this, arguments);
        640 } catch (e) {
        641 self.dbgLog_('Wrapping fail()');
        642 self.doAsyncError(e);
        643 }
        644 };
        645 }
        646};
        647
        648
        649/**
        650 * Sets a window.onerror handler for catching exceptions that happen in async
        651 * callbacks. Note that as of Safari 3.1, Safari does not support this.
        652 * @private
        653 */
        654goog.testing.AsyncTestCase.prototype.hookOnError_ = function() {
        655 if (!this.origOnError_) {
        656 this.origOnError_ = window.onerror;
        657 var self = this;
        658 window.onerror = function(error, url, line) {
        659 // Ignore exceptions that we threw on purpose.
        660 var cbe =
        661 goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
        662 if (String(error).indexOf(cbe) != -1 &&
        663 self.numControlExceptionsExpected_) {
        664 self.numControlExceptionsExpected_ -= 1;
        665 self.dbgLog_('window.onerror: numControlExceptionsExpected_ = ' +
        666 self.numControlExceptionsExpected_ + ' and ignoring exception. ' +
        667 error);
        668 // Tell the browser not to compain about the error.
        669 return true;
        670 } else {
        671 self.dbgLog_('window.onerror caught exception.');
        672 var message = error + '\nURL: ' + url + '\nLine: ' + line;
        673 self.doTopOfStackAsyncError_(message);
        674 // Tell the browser to complain about the error.
        675 return false;
        676 }
        677 };
        678 }
        679};
        680
        681
        682/**
        683 * Unhooks window.onerror and _assert.
        684 * @private
        685 */
        686goog.testing.AsyncTestCase.prototype.unhookAll_ = function() {
        687 if (this.origOnError_) {
        688 window.onerror = this.origOnError_;
        689 this.origOnError_ = null;
        690 _assert = this.origAssert_;
        691 this.origAssert_ = null;
        692 fail = this.origFail_;
        693 this.origFail_ = null;
        694 }
        695};
        696
        697
        698/**
        699 * Enables the timeout timer. This timer fires unless continueTesting is
        700 * called.
        701 * @private
        702 */
        703goog.testing.AsyncTestCase.prototype.startTimeoutTimer_ = function() {
        704 if (!this.timeoutHandle_ && this.stepTimeout > 0) {
        705 this.timeoutHandle_ = this.timeout(goog.bind(function() {
        706 this.dbgLog_('Timeout timer fired with id ' + this.timeoutHandle_);
        707 this.timeoutHandle_ = 0;
        708
        709 this.doTopOfStackAsyncError_('Timed out while waiting for ' +
        710 'continueTesting() to be called.');
        711 }, this, null), this.stepTimeout);
        712 this.dbgLog_('Started timeout timer with id ' + this.timeoutHandle_);
        713 }
        714};
        715
        716
        717/**
        718 * Disables the timeout timer.
        719 * @private
        720 */
        721goog.testing.AsyncTestCase.prototype.stopTimeoutTimer_ = function() {
        722 if (this.timeoutHandle_) {
        723 this.dbgLog_('Clearing timeout timer with id ' + this.timeoutHandle_);
        724 this.clearTimeout(this.timeoutHandle_);
        725 this.timeoutHandle_ = 0;
        726 }
        727};
        728
        729
        730/**
        731 * Sets the next function to call in our sequence of async callbacks.
        732 * @param {Function} func The function that executes the next step.
        733 * @param {string} name A description of the next step.
        734 * @private
        735 */
        736goog.testing.AsyncTestCase.prototype.setNextStep_ = function(func, name) {
        737 this.nextStepFunc_ = func && goog.bind(func, this);
        738 this.nextStepName_ = name;
        739};
        740
        741
        742/**
        743 * Calls the given function, redirecting any exceptions to doAsyncError.
        744 * @param {Function} func The function to call.
        745 * @return {!goog.testing.AsyncTestCase.TopStackFuncResult_} Returns a
        746 * TopStackFuncResult_.
        747 * @private
        748 */
        749goog.testing.AsyncTestCase.prototype.callTopOfStackFunc_ = function(func) {
        750 /** @preserveTry */
        751 try {
        752 func.call(this);
        753 return {controlBreakingExceptionThrown: false, message: ''};
        754 } catch (e) {
        755 this.dbgLog_('Caught exception in callTopOfStackFunc_');
        756 /** @preserveTry */
        757 try {
        758 this.doAsyncError(e);
        759 return {controlBreakingExceptionThrown: false, message: ''};
        760 } catch (e2) {
        761 if (!e2.isControlBreakingException) {
        762 throw e2;
        763 }
        764 return {controlBreakingExceptionThrown: true, message: e2.message};
        765 }
        766 }
        767};
        768
        769
        770/**
        771 * Calls the next callback when the isReady_ flag is true.
        772 * @param {Function=} opt_doFirst A function to call before pumping.
        773 * @private
        774 * @throws Throws a ControlBreakingException if there were any failing steps.
        775 */
        776goog.testing.AsyncTestCase.prototype.pump_ = function(opt_doFirst) {
        777 // If this function is already above us in the call-stack, then we should
        778 // return rather than pumping in order to minimize call-stack depth.
        779 if (!this.returnWillPump_) {
        780 this.setBatchTime(this.now());
        781 this.returnWillPump_ = true;
        782 var topFuncResult = {};
        783
        784 if (opt_doFirst) {
        785 topFuncResult = this.callTopOfStackFunc_(opt_doFirst);
        786 }
        787 // Note: we don't check for this.running here because it is not set to true
        788 // while executing setUpPage and tearDownPage.
        789 // Also, if isReady_ is false, then one of two things will happen:
        790 // 1. Our timeout callback will be called.
        791 // 2. The tests will call continueTesting(), which will call pump_() again.
        792 while (this.isReady_ && this.nextStepFunc_ &&
        793 !topFuncResult.controlBreakingExceptionThrown) {
        794 this.curStepFunc_ = this.nextStepFunc_;
        795 this.curStepName_ = this.nextStepName_;
        796 this.nextStepFunc_ = null;
        797 this.nextStepName_ = '';
        798
        799 this.dbgLog_('Performing step: ' + this.curStepName_);
        800 topFuncResult =
        801 this.callTopOfStackFunc_(/** @type {Function} */(this.curStepFunc_));
        802
        803 // If the max run time is exceeded call this function again async so as
        804 // not to block the browser.
        805 var delta = this.now() - this.getBatchTime();
        806 if (delta > goog.testing.TestCase.maxRunTime &&
        807 !topFuncResult.controlBreakingExceptionThrown) {
        808 this.saveMessage('Breaking async');
        809 var self = this;
        810 this.timeout(function() { self.pump_(); }, 100);
        811 break;
        812 }
        813 }
        814 this.returnWillPump_ = false;
        815 } else if (opt_doFirst) {
        816 opt_doFirst.call(this);
        817 }
        818};
        819
        820
        821/**
        822 * Sets up the test page and then waits untill the test case has been marked
        823 * as ready before executing the tests.
        824 * @private
        825 */
        826goog.testing.AsyncTestCase.prototype.doSetUpPage_ = function() {
        827 this.setNextStep_(this.execute, 'TestCase.execute');
        828 this.setUpPage();
        829};
        830
        831
        832/**
        833 * Step 1: Move to the next test.
        834 * @private
        835 */
        836goog.testing.AsyncTestCase.prototype.doIteration_ = function() {
        837 this.expectedSignalCount_ = 0;
        838 this.receivedSignalCount_ = 0;
        839 this.activeTest = this.next();
        840 if (this.activeTest && this.running) {
        841 this.result_.runCount++;
        842 // If this test should be marked as having failed, doIteration will go
        843 // straight to the next test.
        844 if (this.maybeFailTestEarly(this.activeTest)) {
        845 this.setNextStep_(this.doIteration_, 'doIteration');
        846 } else {
        847 this.setNextStep_(this.doSetUp_, 'setUp');
        848 }
        849 } else {
        850 // All tests done.
        851 this.finalize();
        852 }
        853};
        854
        855
        856/**
        857 * Step 2: Call setUp().
        858 * @private
        859 */
        860goog.testing.AsyncTestCase.prototype.doSetUp_ = function() {
        861 this.log('Running test: ' + this.activeTest.name);
        862 this.cleanedUp_ = false;
        863 this.setNextStep_(this.doExecute_, this.activeTest.name);
        864 this.setUp();
        865};
        866
        867
        868/**
        869 * Step 3: Call test.execute().
        870 * @private
        871 */
        872goog.testing.AsyncTestCase.prototype.doExecute_ = function() {
        873 this.setNextStep_(this.doTearDown_, 'tearDown');
        874 this.activeTest.execute();
        875};
        876
        877
        878/**
        879 * Step 4: Call tearDown().
        880 * @private
        881 */
        882goog.testing.AsyncTestCase.prototype.doTearDown_ = function() {
        883 this.cleanedUp_ = true;
        884 this.setNextStep_(this.doNext_, 'doNext');
        885 this.tearDown();
        886};
        887
        888
        889/**
        890 * Step 5: Call doSuccess()
        891 * @private
        892 */
        893goog.testing.AsyncTestCase.prototype.doNext_ = function() {
        894 this.setNextStep_(this.doIteration_, 'doIteration');
        895 this.doSuccess(/** @type {goog.testing.TestCase.Test} */(this.activeTest));
        896};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/events/events.js.src.html b/docs/source/lib/goog/testing/events/events.js.src.html new file mode 100644 index 0000000..c0627db --- /dev/null +++ b/docs/source/lib/goog/testing/events/events.js.src.html @@ -0,0 +1 @@ +events.js

        lib/goog/testing/events/events.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 Event Simulation.
        17 *
        18 * Utility functions for simulating events at the Closure level. All functions
        19 * in this package generate events by calling goog.events.fireListeners,
        20 * rather than interfacing with the browser directly. This is intended for
        21 * testing purposes, and should not be used in production code.
        22 *
        23 * The decision to use Closure events and dispatchers instead of the browser's
        24 * native events and dispatchers was conscious and deliberate. Native event
        25 * dispatchers have their own set of quirks and edge cases. Pure JS dispatchers
        26 * are more robust and transparent.
        27 *
        28 * If you think you need a testing mechanism that uses native Event objects,
        29 * please, please email closure-tech first to explain your use case before you
        30 * sink time into this.
        31 *
        32 * @author nicksantos@google.com (Nick Santos)
        33 */
        34
        35goog.provide('goog.testing.events');
        36goog.provide('goog.testing.events.Event');
        37
        38goog.require('goog.Disposable');
        39goog.require('goog.asserts');
        40goog.require('goog.dom.NodeType');
        41goog.require('goog.events');
        42goog.require('goog.events.BrowserEvent');
        43goog.require('goog.events.BrowserFeature');
        44goog.require('goog.events.EventTarget');
        45goog.require('goog.events.EventType');
        46goog.require('goog.events.KeyCodes');
        47goog.require('goog.object');
        48goog.require('goog.style');
        49goog.require('goog.userAgent');
        50
        51
        52
        53/**
        54 * goog.events.BrowserEvent expects an Event so we provide one for JSCompiler.
        55 *
        56 * This clones a lot of the functionality of goog.events.Event. This used to
        57 * use a mixin, but the mixin results in confusing the two types when compiled.
        58 *
        59 * @param {string} type Event Type.
        60 * @param {Object=} opt_target Reference to the object that is the target of
        61 * this event.
        62 * @constructor
        63 * @extends {Event}
        64 */
        65goog.testing.events.Event = function(type, opt_target) {
        66 this.type = type;
        67
        68 this.target = /** @type {EventTarget} */ (opt_target || null);
        69
        70 this.currentTarget = this.target;
        71};
        72
        73
        74/**
        75 * Whether to cancel the event in internal capture/bubble processing for IE.
        76 * @type {boolean}
        77 * @public
        78 * @suppress {underscore|visibility} Technically public, but referencing this
        79 * outside this package is strongly discouraged.
        80 */
        81goog.testing.events.Event.prototype.propagationStopped_ = false;
        82
        83
        84/** @override */
        85goog.testing.events.Event.prototype.defaultPrevented = false;
        86
        87
        88/**
        89 * Return value for in internal capture/bubble processing for IE.
        90 * @type {boolean}
        91 * @public
        92 * @suppress {underscore|visibility} Technically public, but referencing this
        93 * outside this package is strongly discouraged.
        94 */
        95goog.testing.events.Event.prototype.returnValue_ = true;
        96
        97
        98/** @override */
        99goog.testing.events.Event.prototype.stopPropagation = function() {
        100 this.propagationStopped_ = true;
        101};
        102
        103
        104/** @override */
        105goog.testing.events.Event.prototype.preventDefault = function() {
        106 this.defaultPrevented = true;
        107 this.returnValue_ = false;
        108};
        109
        110
        111/**
        112 * Asserts an event target exists. This will fail if target is not defined.
        113 *
        114 * TODO(nnaze): Gradually add this to the methods in this file, and eventually
        115 * update the method signatures to not take nullables. See http://b/8961907
        116 *
        117 * @param {EventTarget} target A target to assert.
        118 * @return {!EventTarget} The target, guaranteed to exist.
        119 * @private
        120 */
        121goog.testing.events.assertEventTarget_ = function(target) {
        122 return goog.asserts.assert(target, 'EventTarget should be defined.');
        123};
        124
        125
        126/**
        127 * A static helper function that sets the mouse position to the event.
        128 * @param {Event} event A simulated native event.
        129 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        130 * target's position (if available), otherwise (0, 0).
        131 * @private
        132 */
        133goog.testing.events.setEventClientXY_ = function(event, opt_coords) {
        134 if (!opt_coords && event.target &&
        135 event.target.nodeType == goog.dom.NodeType.ELEMENT) {
        136 try {
        137 opt_coords =
        138 goog.style.getClientPosition(/** @type {!Element} **/ (event.target));
        139 } catch (ex) {
        140 // IE sometimes throws if it can't get the position.
        141 }
        142 }
        143 event.clientX = opt_coords ? opt_coords.x : 0;
        144 event.clientY = opt_coords ? opt_coords.y : 0;
        145
        146 // Pretend the browser window is at (0, 0).
        147 event.screenX = event.clientX;
        148 event.screenY = event.clientY;
        149};
        150
        151
        152/**
        153 * Simulates a mousedown, mouseup, and then click on the given event target,
        154 * with the left mouse button.
        155 * @param {EventTarget} target The target for the event.
        156 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
        157 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
        158 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        159 * target's position (if available), otherwise (0, 0).
        160 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        161 * BrowserEvent.
        162 * @return {boolean} The returnValue of the sequence: false if preventDefault()
        163 * was called on any of the events, true otherwise.
        164 */
        165goog.testing.events.fireClickSequence =
        166 function(target, opt_button, opt_coords, opt_eventProperties) {
        167 // Fire mousedown, mouseup, and click. Then return the bitwise AND of the 3.
        168 return !!(goog.testing.events.fireMouseDownEvent(
        169 target, opt_button, opt_coords, opt_eventProperties) &
        170 goog.testing.events.fireMouseUpEvent(
        171 target, opt_button, opt_coords, opt_eventProperties) &
        172 goog.testing.events.fireClickEvent(
        173 target, opt_button, opt_coords, opt_eventProperties));
        174};
        175
        176
        177/**
        178 * Simulates the sequence of events fired by the browser when the user double-
        179 * clicks the given target.
        180 * @param {EventTarget} target The target for the event.
        181 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        182 * target's position (if available), otherwise (0, 0).
        183 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        184 * BrowserEvent.
        185 * @return {boolean} The returnValue of the sequence: false if preventDefault()
        186 * was called on any of the events, true otherwise.
        187 */
        188goog.testing.events.fireDoubleClickSequence = function(
        189 target, opt_coords, opt_eventProperties) {
        190 // Fire mousedown, mouseup, click, mousedown, mouseup, click, dblclick.
        191 // Then return the bitwise AND of the 7.
        192 var btn = goog.events.BrowserEvent.MouseButton.LEFT;
        193 return !!(goog.testing.events.fireMouseDownEvent(
        194 target, btn, opt_coords, opt_eventProperties) &
        195 goog.testing.events.fireMouseUpEvent(
        196 target, btn, opt_coords, opt_eventProperties) &
        197 goog.testing.events.fireClickEvent(
        198 target, btn, opt_coords, opt_eventProperties) &
        199 // IE fires a selectstart instead of the second mousedown in a
        200 // dblclick, but we don't care about selectstart.
        201 (goog.userAgent.IE ||
        202 goog.testing.events.fireMouseDownEvent(
        203 target, btn, opt_coords, opt_eventProperties)) &
        204 goog.testing.events.fireMouseUpEvent(
        205 target, btn, opt_coords, opt_eventProperties) &
        206 // IE doesn't fire the second click in a dblclick.
        207 (goog.userAgent.IE ||
        208 goog.testing.events.fireClickEvent(
        209 target, btn, opt_coords, opt_eventProperties)) &
        210 goog.testing.events.fireDoubleClickEvent(
        211 target, opt_coords, opt_eventProperties));
        212};
        213
        214
        215/**
        216 * Simulates a complete keystroke (keydown, keypress, and keyup). Note that
        217 * if preventDefault is called on the keydown, the keypress will not fire.
        218 *
        219 * @param {EventTarget} target The target for the event.
        220 * @param {number} keyCode The keycode of the key pressed.
        221 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        222 * BrowserEvent.
        223 * @return {boolean} The returnValue of the sequence: false if preventDefault()
        224 * was called on any of the events, true otherwise.
        225 */
        226goog.testing.events.fireKeySequence = function(
        227 target, keyCode, opt_eventProperties) {
        228 return goog.testing.events.fireNonAsciiKeySequence(target, keyCode, keyCode,
        229 opt_eventProperties);
        230};
        231
        232
        233/**
        234 * Simulates a complete keystroke (keydown, keypress, and keyup) when typing
        235 * a non-ASCII character. Same as fireKeySequence, the keypress will not fire
        236 * if preventDefault is called on the keydown.
        237 *
        238 * @param {EventTarget} target The target for the event.
        239 * @param {number} keyCode The keycode of the keydown and keyup events.
        240 * @param {number} keyPressKeyCode The keycode of the keypress event.
        241 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        242 * BrowserEvent.
        243 * @return {boolean} The returnValue of the sequence: false if preventDefault()
        244 * was called on any of the events, true otherwise.
        245 */
        246goog.testing.events.fireNonAsciiKeySequence = function(
        247 target, keyCode, keyPressKeyCode, opt_eventProperties) {
        248 var keydown =
        249 new goog.testing.events.Event(goog.events.EventType.KEYDOWN, target);
        250 var keyup =
        251 new goog.testing.events.Event(goog.events.EventType.KEYUP, target);
        252 var keypress =
        253 new goog.testing.events.Event(goog.events.EventType.KEYPRESS, target);
        254 keydown.keyCode = keyup.keyCode = keyCode;
        255 keypress.keyCode = keyPressKeyCode;
        256
        257 if (opt_eventProperties) {
        258 goog.object.extend(keydown, opt_eventProperties);
        259 goog.object.extend(keyup, opt_eventProperties);
        260 goog.object.extend(keypress, opt_eventProperties);
        261 }
        262
        263 // Fire keydown, keypress, and keyup. Note that if the keydown is
        264 // prevent-defaulted, then the keypress will not fire.
        265 var result = true;
        266 if (!goog.testing.events.isBrokenGeckoMacActionKey_(keydown)) {
        267 result = goog.testing.events.fireBrowserEvent(keydown);
        268 }
        269 if (goog.events.KeyCodes.firesKeyPressEvent(
        270 keyCode, undefined, keydown.shiftKey, keydown.ctrlKey,
        271 keydown.altKey) && result) {
        272 result &= goog.testing.events.fireBrowserEvent(keypress);
        273 }
        274 return !!(result & goog.testing.events.fireBrowserEvent(keyup));
        275};
        276
        277
        278/**
        279 * @param {goog.testing.events.Event} e The event.
        280 * @return {boolean} Whether this is the Gecko/Mac's Meta-C/V/X, which
        281 * is broken and requires special handling.
        282 * @private
        283 */
        284goog.testing.events.isBrokenGeckoMacActionKey_ = function(e) {
        285 return goog.userAgent.MAC && goog.userAgent.GECKO &&
        286 (e.keyCode == goog.events.KeyCodes.C ||
        287 e.keyCode == goog.events.KeyCodes.X ||
        288 e.keyCode == goog.events.KeyCodes.V) && e.metaKey;
        289};
        290
        291
        292/**
        293 * Simulates a mouseenter event on the given target.
        294 * @param {!EventTarget} target The target for the event.
        295 * @param {?EventTarget} relatedTarget The related target for the event (e.g.,
        296 * the node that the mouse is being moved out of).
        297 * @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to
        298 * event's target's position (if available), otherwise (0, 0).
        299 * @return {boolean} The returnValue of the event: false if preventDefault() was
        300 * called on it, true otherwise.
        301 */
        302goog.testing.events.fireMouseEnterEvent = function(target, relatedTarget,
        303 opt_coords) {
        304 var mouseenter =
        305 new goog.testing.events.Event(goog.events.EventType.MOUSEENTER, target);
        306 mouseenter.relatedTarget = relatedTarget;
        307 goog.testing.events.setEventClientXY_(mouseenter, opt_coords);
        308 return goog.testing.events.fireBrowserEvent(mouseenter);
        309};
        310
        311
        312/**
        313 * Simulates a mouseleave event on the given target.
        314 * @param {!EventTarget} target The target for the event.
        315 * @param {?EventTarget} relatedTarget The related target for the event (e.g.,
        316 * the node that the mouse is being moved into).
        317 * @param {!goog.math.Coordinate=} opt_coords Mouse position. Defaults to
        318 * event's target's position (if available), otherwise (0, 0).
        319 * @return {boolean} The returnValue of the event: false if preventDefault() was
        320 * called on it, true otherwise.
        321 */
        322goog.testing.events.fireMouseLeaveEvent = function(target, relatedTarget,
        323 opt_coords) {
        324 var mouseleave =
        325 new goog.testing.events.Event(goog.events.EventType.MOUSELEAVE, target);
        326 mouseleave.relatedTarget = relatedTarget;
        327 goog.testing.events.setEventClientXY_(mouseleave, opt_coords);
        328 return goog.testing.events.fireBrowserEvent(mouseleave);
        329};
        330
        331
        332/**
        333 * Simulates a mouseover event on the given target.
        334 * @param {EventTarget} target The target for the event.
        335 * @param {EventTarget} relatedTarget The related target for the event (e.g.,
        336 * the node that the mouse is being moved out of).
        337 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        338 * target's position (if available), otherwise (0, 0).
        339 * @return {boolean} The returnValue of the event: false if preventDefault() was
        340 * called on it, true otherwise.
        341 */
        342goog.testing.events.fireMouseOverEvent = function(target, relatedTarget,
        343 opt_coords) {
        344 var mouseover =
        345 new goog.testing.events.Event(goog.events.EventType.MOUSEOVER, target);
        346 mouseover.relatedTarget = relatedTarget;
        347 goog.testing.events.setEventClientXY_(mouseover, opt_coords);
        348 return goog.testing.events.fireBrowserEvent(mouseover);
        349};
        350
        351
        352/**
        353 * Simulates a mousemove event on the given target.
        354 * @param {EventTarget} target The target for the event.
        355 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        356 * target's position (if available), otherwise (0, 0).
        357 * @return {boolean} The returnValue of the event: false if preventDefault() was
        358 * called on it, true otherwise.
        359 */
        360goog.testing.events.fireMouseMoveEvent = function(target, opt_coords) {
        361 var mousemove =
        362 new goog.testing.events.Event(goog.events.EventType.MOUSEMOVE, target);
        363
        364 goog.testing.events.setEventClientXY_(mousemove, opt_coords);
        365 return goog.testing.events.fireBrowserEvent(mousemove);
        366};
        367
        368
        369/**
        370 * Simulates a mouseout event on the given target.
        371 * @param {EventTarget} target The target for the event.
        372 * @param {EventTarget} relatedTarget The related target for the event (e.g.,
        373 * the node that the mouse is being moved into).
        374 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        375 * target's position (if available), otherwise (0, 0).
        376 * @return {boolean} The returnValue of the event: false if preventDefault() was
        377 * called on it, true otherwise.
        378 */
        379goog.testing.events.fireMouseOutEvent = function(target, relatedTarget,
        380 opt_coords) {
        381 var mouseout =
        382 new goog.testing.events.Event(goog.events.EventType.MOUSEOUT, target);
        383 mouseout.relatedTarget = relatedTarget;
        384 goog.testing.events.setEventClientXY_(mouseout, opt_coords);
        385 return goog.testing.events.fireBrowserEvent(mouseout);
        386};
        387
        388
        389/**
        390 * Simulates a mousedown event on the given target.
        391 * @param {EventTarget} target The target for the event.
        392 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
        393 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
        394 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        395 * target's position (if available), otherwise (0, 0).
        396 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        397 * BrowserEvent.
        398 * @return {boolean} The returnValue of the event: false if preventDefault() was
        399 * called on it, true otherwise.
        400 */
        401goog.testing.events.fireMouseDownEvent =
        402 function(target, opt_button, opt_coords, opt_eventProperties) {
        403
        404 var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
        405 button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
        406 goog.events.BrowserEvent.IEButtonMap[button] : button;
        407 return goog.testing.events.fireMouseButtonEvent_(
        408 goog.events.EventType.MOUSEDOWN, target, button, opt_coords,
        409 opt_eventProperties);
        410};
        411
        412
        413/**
        414 * Simulates a mouseup event on the given target.
        415 * @param {EventTarget} target The target for the event.
        416 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
        417 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
        418 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        419 * target's position (if available), otherwise (0, 0).
        420 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        421 * BrowserEvent.
        422 * @return {boolean} The returnValue of the event: false if preventDefault() was
        423 * called on it, true otherwise.
        424 */
        425goog.testing.events.fireMouseUpEvent =
        426 function(target, opt_button, opt_coords, opt_eventProperties) {
        427 var button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
        428 button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
        429 goog.events.BrowserEvent.IEButtonMap[button] : button;
        430 return goog.testing.events.fireMouseButtonEvent_(
        431 goog.events.EventType.MOUSEUP, target, button, opt_coords,
        432 opt_eventProperties);
        433};
        434
        435
        436/**
        437 * Simulates a click event on the given target. IE only supports click with
        438 * the left mouse button.
        439 * @param {EventTarget} target The target for the event.
        440 * @param {goog.events.BrowserEvent.MouseButton=} opt_button Mouse button;
        441 * defaults to {@code goog.events.BrowserEvent.MouseButton.LEFT}.
        442 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        443 * target's position (if available), otherwise (0, 0).
        444 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        445 * BrowserEvent.
        446 * @return {boolean} The returnValue of the event: false if preventDefault() was
        447 * called on it, true otherwise.
        448 */
        449goog.testing.events.fireClickEvent =
        450 function(target, opt_button, opt_coords, opt_eventProperties) {
        451 return goog.testing.events.fireMouseButtonEvent_(goog.events.EventType.CLICK,
        452 target, opt_button, opt_coords, opt_eventProperties);
        453};
        454
        455
        456/**
        457 * Simulates a double-click event on the given target. Always double-clicks
        458 * with the left mouse button since no browser supports double-clicking with
        459 * any other buttons.
        460 * @param {EventTarget} target The target for the event.
        461 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        462 * target's position (if available), otherwise (0, 0).
        463 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        464 * BrowserEvent.
        465 * @return {boolean} The returnValue of the event: false if preventDefault() was
        466 * called on it, true otherwise.
        467 */
        468goog.testing.events.fireDoubleClickEvent =
        469 function(target, opt_coords, opt_eventProperties) {
        470 return goog.testing.events.fireMouseButtonEvent_(
        471 goog.events.EventType.DBLCLICK, target,
        472 goog.events.BrowserEvent.MouseButton.LEFT, opt_coords,
        473 opt_eventProperties);
        474};
        475
        476
        477/**
        478 * Helper function to fire a mouse event.
        479 * with the left mouse button since no browser supports double-clicking with
        480 * any other buttons.
        481 * @param {string} type The event type.
        482 * @param {EventTarget} target The target for the event.
        483 * @param {number=} opt_button Mouse button; defaults to
        484 * {@code goog.events.BrowserEvent.MouseButton.LEFT}.
        485 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        486 * target's position (if available), otherwise (0, 0).
        487 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        488 * BrowserEvent.
        489 * @return {boolean} The returnValue of the event: false if preventDefault() was
        490 * called on it, true otherwise.
        491 * @private
        492 */
        493goog.testing.events.fireMouseButtonEvent_ =
        494 function(type, target, opt_button, opt_coords, opt_eventProperties) {
        495 var e =
        496 new goog.testing.events.Event(type, target);
        497 e.button = opt_button || goog.events.BrowserEvent.MouseButton.LEFT;
        498 goog.testing.events.setEventClientXY_(e, opt_coords);
        499 if (opt_eventProperties) {
        500 goog.object.extend(e, opt_eventProperties);
        501 }
        502 return goog.testing.events.fireBrowserEvent(e);
        503};
        504
        505
        506/**
        507 * Simulates a contextmenu event on the given target.
        508 * @param {EventTarget} target The target for the event.
        509 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        510 * target's position (if available), otherwise (0, 0).
        511 * @return {boolean} The returnValue of the event: false if preventDefault() was
        512 * called on it, true otherwise.
        513 */
        514goog.testing.events.fireContextMenuEvent = function(target, opt_coords) {
        515 var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
        516 goog.events.BrowserEvent.MouseButton.LEFT :
        517 goog.events.BrowserEvent.MouseButton.RIGHT;
        518 var contextmenu =
        519 new goog.testing.events.Event(goog.events.EventType.CONTEXTMENU, target);
        520 contextmenu.button = !goog.events.BrowserFeature.HAS_W3C_BUTTON ?
        521 goog.events.BrowserEvent.IEButtonMap[button] : button;
        522 contextmenu.ctrlKey = goog.userAgent.MAC;
        523 goog.testing.events.setEventClientXY_(contextmenu, opt_coords);
        524 return goog.testing.events.fireBrowserEvent(contextmenu);
        525};
        526
        527
        528/**
        529 * Simulates a mousedown, contextmenu, and the mouseup on the given event
        530 * target, with the right mouse button.
        531 * @param {EventTarget} target The target for the event.
        532 * @param {goog.math.Coordinate=} opt_coords Mouse position. Defaults to event's
        533 * target's position (if available), otherwise (0, 0).
        534 * @return {boolean} The returnValue of the sequence: false if preventDefault()
        535 * was called on any of the events, true otherwise.
        536 */
        537goog.testing.events.fireContextMenuSequence = function(target, opt_coords) {
        538 var props = goog.userAgent.MAC ? {ctrlKey: true} : {};
        539 var button = (goog.userAgent.MAC && goog.userAgent.WEBKIT) ?
        540 goog.events.BrowserEvent.MouseButton.LEFT :
        541 goog.events.BrowserEvent.MouseButton.RIGHT;
        542
        543 var result = goog.testing.events.fireMouseDownEvent(target,
        544 button, opt_coords, props);
        545 if (goog.userAgent.WINDOWS) {
        546 // All browsers are consistent on Windows.
        547 result &= goog.testing.events.fireMouseUpEvent(target,
        548 button, opt_coords) &
        549 goog.testing.events.fireContextMenuEvent(target, opt_coords);
        550 } else {
        551 result &= goog.testing.events.fireContextMenuEvent(target, opt_coords);
        552
        553 // GECKO on Mac and Linux always fires the mouseup after the contextmenu.
        554
        555 // WEBKIT is really weird.
        556 //
        557 // On Linux, it sometimes fires mouseup, but most of the time doesn't.
        558 // It's really hard to reproduce consistently. I think there's some
        559 // internal race condition. If contextmenu is preventDefaulted, then
        560 // mouseup always fires.
        561 //
        562 // On Mac, it always fires mouseup and then fires a click.
        563 result &= goog.testing.events.fireMouseUpEvent(target,
        564 button, opt_coords, props);
        565
        566 if (goog.userAgent.WEBKIT && goog.userAgent.MAC) {
        567 result &= goog.testing.events.fireClickEvent(
        568 target, button, opt_coords, props);
        569 }
        570 }
        571 return !!result;
        572};
        573
        574
        575/**
        576 * Simulates a popstate event on the given target.
        577 * @param {EventTarget} target The target for the event.
        578 * @param {Object} state History state object.
        579 * @return {boolean} The returnValue of the event: false if preventDefault() was
        580 * called on it, true otherwise.
        581 */
        582goog.testing.events.firePopStateEvent = function(target, state) {
        583 var e = new goog.testing.events.Event(goog.events.EventType.POPSTATE, target);
        584 e.state = state;
        585 return goog.testing.events.fireBrowserEvent(e);
        586};
        587
        588
        589/**
        590 * Simulate a blur event on the given target.
        591 * @param {EventTarget} target The target for the event.
        592 * @return {boolean} The value returned by firing the blur browser event,
        593 * which returns false iff 'preventDefault' was invoked.
        594 */
        595goog.testing.events.fireBlurEvent = function(target) {
        596 var e = new goog.testing.events.Event(
        597 goog.events.EventType.BLUR, target);
        598 return goog.testing.events.fireBrowserEvent(e);
        599};
        600
        601
        602/**
        603 * Simulate a focus event on the given target.
        604 * @param {EventTarget} target The target for the event.
        605 * @return {boolean} The value returned by firing the focus browser event,
        606 * which returns false iff 'preventDefault' was invoked.
        607 */
        608goog.testing.events.fireFocusEvent = function(target) {
        609 var e = new goog.testing.events.Event(
        610 goog.events.EventType.FOCUS, target);
        611 return goog.testing.events.fireBrowserEvent(e);
        612};
        613
        614
        615/**
        616 * Simulates an event's capturing and bubbling phases.
        617 * @param {Event} event A simulated native event. It will be wrapped in a
        618 * normalized BrowserEvent and dispatched to Closure listeners on all
        619 * ancestors of its target (inclusive).
        620 * @return {boolean} The returnValue of the event: false if preventDefault() was
        621 * called on it, true otherwise.
        622 */
        623goog.testing.events.fireBrowserEvent = function(event) {
        624 event.returnValue_ = true;
        625
        626 // generate a list of ancestors
        627 var ancestors = [];
        628 for (var current = event.target; current; current = current.parentNode) {
        629 ancestors.push(current);
        630 }
        631
        632 // dispatch capturing listeners
        633 for (var j = ancestors.length - 1;
        634 j >= 0 && !event.propagationStopped_;
        635 j--) {
        636 goog.events.fireListeners(ancestors[j], event.type, true,
        637 new goog.events.BrowserEvent(event, ancestors[j]));
        638 }
        639
        640 // dispatch bubbling listeners
        641 for (var j = 0;
        642 j < ancestors.length && !event.propagationStopped_;
        643 j++) {
        644 goog.events.fireListeners(ancestors[j], event.type, false,
        645 new goog.events.BrowserEvent(event, ancestors[j]));
        646 }
        647
        648 return event.returnValue_;
        649};
        650
        651
        652/**
        653 * Simulates a touchstart event on the given target.
        654 * @param {EventTarget} target The target for the event.
        655 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
        656 * target's position (if available), otherwise (0, 0).
        657 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        658 * BrowserEvent.
        659 * @return {boolean} The returnValue of the event: false if preventDefault() was
        660 * called on it, true otherwise.
        661 */
        662goog.testing.events.fireTouchStartEvent = function(
        663 target, opt_coords, opt_eventProperties) {
        664 // TODO: Support multi-touch events with array of coordinates.
        665 var touchstart =
        666 new goog.testing.events.Event(goog.events.EventType.TOUCHSTART, target);
        667 goog.testing.events.setEventClientXY_(touchstart, opt_coords);
        668 if (opt_eventProperties) {
        669 goog.object.extend(touchstart, opt_eventProperties);
        670 }
        671 return goog.testing.events.fireBrowserEvent(touchstart);
        672};
        673
        674
        675/**
        676 * Simulates a touchmove event on the given target.
        677 * @param {EventTarget} target The target for the event.
        678 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
        679 * target's position (if available), otherwise (0, 0).
        680 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        681 * BrowserEvent.
        682 * @return {boolean} The returnValue of the event: false if preventDefault() was
        683 * called on it, true otherwise.
        684 */
        685goog.testing.events.fireTouchMoveEvent = function(
        686 target, opt_coords, opt_eventProperties) {
        687 // TODO: Support multi-touch events with array of coordinates.
        688 var touchmove =
        689 new goog.testing.events.Event(goog.events.EventType.TOUCHMOVE, target);
        690 goog.testing.events.setEventClientXY_(touchmove, opt_coords);
        691 if (opt_eventProperties) {
        692 goog.object.extend(touchmove, opt_eventProperties);
        693 }
        694 return goog.testing.events.fireBrowserEvent(touchmove);
        695};
        696
        697
        698/**
        699 * Simulates a touchend event on the given target.
        700 * @param {EventTarget} target The target for the event.
        701 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event's
        702 * target's position (if available), otherwise (0, 0).
        703 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        704 * BrowserEvent.
        705 * @return {boolean} The returnValue of the event: false if preventDefault() was
        706 * called on it, true otherwise.
        707 */
        708goog.testing.events.fireTouchEndEvent = function(
        709 target, opt_coords, opt_eventProperties) {
        710 // TODO: Support multi-touch events with array of coordinates.
        711 var touchend =
        712 new goog.testing.events.Event(goog.events.EventType.TOUCHEND, target);
        713 goog.testing.events.setEventClientXY_(touchend, opt_coords);
        714 if (opt_eventProperties) {
        715 goog.object.extend(touchend, opt_eventProperties);
        716 }
        717 return goog.testing.events.fireBrowserEvent(touchend);
        718};
        719
        720
        721/**
        722 * Simulates a simple touch sequence on the given target.
        723 * @param {EventTarget} target The target for the event.
        724 * @param {goog.math.Coordinate=} opt_coords Touch position. Defaults to event
        725 * target's position (if available), otherwise (0, 0).
        726 * @param {Object=} opt_eventProperties Event properties to be mixed into the
        727 * BrowserEvent.
        728 * @return {boolean} The returnValue of the sequence: false if preventDefault()
        729 * was called on any of the events, true otherwise.
        730 */
        731goog.testing.events.fireTouchSequence = function(
        732 target, opt_coords, opt_eventProperties) {
        733 // TODO: Support multi-touch events with array of coordinates.
        734 // Fire touchstart, touchmove, touchend then return the bitwise AND of the 3.
        735 return !!(goog.testing.events.fireTouchStartEvent(
        736 target, opt_coords, opt_eventProperties) &
        737 goog.testing.events.fireTouchEndEvent(
        738 target, opt_coords, opt_eventProperties));
        739};
        740
        741
        742/**
        743 * Mixins a listenable into the given object. This turns the object
        744 * into a goog.events.Listenable. This is useful, for example, when
        745 * you need to mock a implementation of listenable and still want it
        746 * to work with goog.events.
        747 * @param {!Object} obj The object to mixin into.
        748 */
        749goog.testing.events.mixinListenable = function(obj) {
        750 var listenable = new goog.events.EventTarget();
        751
        752 listenable.setTargetForTesting(obj);
        753
        754 var listenablePrototype = goog.events.EventTarget.prototype;
        755 var disposablePrototype = goog.Disposable.prototype;
        756 for (var key in listenablePrototype) {
        757 if (listenablePrototype.hasOwnProperty(key) ||
        758 disposablePrototype.hasOwnProperty(key)) {
        759 var member = listenablePrototype[key];
        760 if (goog.isFunction(member)) {
        761 obj[key] = goog.bind(member, listenable);
        762 } else {
        763 obj[key] = member;
        764 }
        765 }
        766 }
        767};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/functionmock.js.src.html b/docs/source/lib/goog/testing/functionmock.js.src.html new file mode 100644 index 0000000..7689b4b --- /dev/null +++ b/docs/source/lib/goog/testing/functionmock.js.src.html @@ -0,0 +1 @@ +functionmock.js

        lib/goog/testing/functionmock.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 Enable mocking of functions not attached to objects
        17 * whether they be global / top-level or anonymous methods / closures.
        18 *
        19 * See the unit tests for usage.
        20 *
        21 */
        22
        23goog.provide('goog.testing');
        24goog.provide('goog.testing.FunctionMock');
        25goog.provide('goog.testing.GlobalFunctionMock');
        26goog.provide('goog.testing.MethodMock');
        27
        28goog.require('goog.object');
        29goog.require('goog.testing.LooseMock');
        30goog.require('goog.testing.Mock');
        31goog.require('goog.testing.PropertyReplacer');
        32goog.require('goog.testing.StrictMock');
        33
        34
        35/**
        36 * Class used to mock a function. Useful for mocking closures and anonymous
        37 * callbacks etc. Creates a function object that extends goog.testing.Mock.
        38 * @param {string=} opt_functionName The optional name of the function to mock.
        39 * Set to '[anonymous mocked function]' if not passed in.
        40 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        41 * goog.testing.Mock.STRICT. The default is STRICT.
        42 * @return {!goog.testing.MockInterface} The mocked function.
        43 * @suppress {missingProperties} Mocks do not fit in the type system well.
        44 */
        45goog.testing.FunctionMock = function(opt_functionName, opt_strictness) {
        46 var fn = function() {
        47 var args = Array.prototype.slice.call(arguments);
        48 args.splice(0, 0, opt_functionName || '[anonymous mocked function]');
        49 return fn.$mockMethod.apply(fn, args);
        50 };
        51 var base = opt_strictness === goog.testing.Mock.LOOSE ?
        52 goog.testing.LooseMock : goog.testing.StrictMock;
        53 goog.object.extend(fn, new base({}));
        54
        55 return /** @type {!goog.testing.MockInterface} */ (fn);
        56};
        57
        58
        59/**
        60 * Mocks an existing function. Creates a goog.testing.FunctionMock
        61 * and registers it in the given scope with the name specified by functionName.
        62 * @param {Object} scope The scope of the method to be mocked out.
        63 * @param {string} functionName The name of the function we're going to mock.
        64 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        65 * goog.testing.Mock.STRICT. The default is STRICT.
        66 * @return {!goog.testing.MockInterface} The mocked method.
        67 */
        68goog.testing.MethodMock = function(scope, functionName, opt_strictness) {
        69 if (!(functionName in scope)) {
        70 throw Error(functionName + ' is not a property of the given scope.');
        71 }
        72
        73 var fn = goog.testing.FunctionMock(functionName, opt_strictness);
        74
        75 fn.$propertyReplacer_ = new goog.testing.PropertyReplacer();
        76 fn.$propertyReplacer_.set(scope, functionName, fn);
        77 fn.$tearDown = goog.testing.MethodMock.$tearDown;
        78
        79 return fn;
        80};
        81
        82
        83/**
        84 * Resets the global function that we mocked back to its original state.
        85 * @this {goog.testing.MockInterface}
        86 */
        87goog.testing.MethodMock.$tearDown = function() {
        88 this.$propertyReplacer_.reset();
        89};
        90
        91
        92/**
        93 * Mocks a global / top-level function. Creates a goog.testing.MethodMock
        94 * in the global scope with the name specified by functionName.
        95 * @param {string} functionName The name of the function we're going to mock.
        96 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        97 * goog.testing.Mock.STRICT. The default is STRICT.
        98 * @return {!goog.testing.MockInterface} The mocked global function.
        99 */
        100goog.testing.GlobalFunctionMock = function(functionName, opt_strictness) {
        101 return goog.testing.MethodMock(goog.global, functionName, opt_strictness);
        102};
        103
        104
        105/**
        106 * Convenience method for creating a mock for a function.
        107 * @param {string=} opt_functionName The optional name of the function to mock
        108 * set to '[anonymous mocked function]' if not passed in.
        109 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        110 * goog.testing.Mock.STRICT. The default is STRICT.
        111 * @return {!goog.testing.MockInterface} The mocked function.
        112 */
        113goog.testing.createFunctionMock = function(opt_functionName, opt_strictness) {
        114 return goog.testing.FunctionMock(opt_functionName, opt_strictness);
        115};
        116
        117
        118/**
        119 * Convenience method for creating a mock for a method.
        120 * @param {Object} scope The scope of the method to be mocked out.
        121 * @param {string} functionName The name of the function we're going to mock.
        122 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        123 * goog.testing.Mock.STRICT. The default is STRICT.
        124 * @return {!goog.testing.MockInterface} The mocked global function.
        125 */
        126goog.testing.createMethodMock = function(scope, functionName, opt_strictness) {
        127 return goog.testing.MethodMock(scope, functionName, opt_strictness);
        128};
        129
        130
        131/**
        132 * Convenience method for creating a mock for a constructor. Copies class
        133 * members to the mock.
        134 *
        135 * <p>When mocking a constructor to return a mocked instance, remember to create
        136 * the instance mock before mocking the constructor. If you mock the constructor
        137 * first, then the mock framework will be unable to examine the prototype chain
        138 * when creating the mock instance.
        139 * @param {Object} scope The scope of the constructor to be mocked out.
        140 * @param {string} constructorName The name of the constructor we're going to
        141 * mock.
        142 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        143 * goog.testing.Mock.STRICT. The default is STRICT.
        144 * @return {!goog.testing.MockInterface} The mocked constructor.
        145 */
        146goog.testing.createConstructorMock = function(scope, constructorName,
        147 opt_strictness) {
        148 var realConstructor = scope[constructorName];
        149 var constructorMock = goog.testing.MethodMock(scope, constructorName,
        150 opt_strictness);
        151
        152 // Copy class members from the real constructor to the mock. Do not copy
        153 // the closure superClass_ property (see goog.inherits), the built-in
        154 // prototype property, or properties added to Function.prototype
        155 for (var property in realConstructor) {
        156 if (property != 'superClass_' &&
        157 property != 'prototype' &&
        158 realConstructor.hasOwnProperty(property)) {
        159 constructorMock[property] = realConstructor[property];
        160 }
        161 }
        162 return constructorMock;
        163};
        164
        165
        166/**
        167 * Convenience method for creating a mocks for a global / top-level function.
        168 * @param {string} functionName The name of the function we're going to mock.
        169 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        170 * goog.testing.Mock.STRICT. The default is STRICT.
        171 * @return {!goog.testing.MockInterface} The mocked global function.
        172 */
        173goog.testing.createGlobalFunctionMock = function(functionName, opt_strictness) {
        174 return goog.testing.GlobalFunctionMock(functionName, opt_strictness);
        175};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/jsunit.js.src.html b/docs/source/lib/goog/testing/jsunit.js.src.html new file mode 100644 index 0000000..d93c3b7 --- /dev/null +++ b/docs/source/lib/goog/testing/jsunit.js.src.html @@ -0,0 +1 @@ +jsunit.js

        lib/goog/testing/jsunit.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 Utilities for working with JsUnit. Writes out the JsUnit file
        17 * that needs to be included in every unit test.
        18 *
        19 * Testing code should not have dependencies outside of goog.testing so as to
        20 * reduce the chance of masking missing dependencies.
        21 *
        22 */
        23
        24goog.provide('goog.testing.jsunit');
        25
        26goog.require('goog.dom.TagName');
        27goog.require('goog.testing.TestCase');
        28goog.require('goog.testing.TestRunner');
        29
        30
        31/**
        32 * Base path for JsUnit app files, relative to Closure's base path.
        33 * @type {string}
        34 */
        35goog.testing.jsunit.BASE_PATH =
        36 '../../third_party/java/jsunit/core/app/';
        37
        38
        39/**
        40 * Filename for the core JS Unit script.
        41 * @type {string}
        42 */
        43goog.testing.jsunit.CORE_SCRIPT =
        44 goog.testing.jsunit.BASE_PATH + 'jsUnitCore.js';
        45
        46
        47/**
        48 * @define {boolean} If this code is being parsed by JsTestC, we let it disable
        49 * the onload handler to avoid running the test in JsTestC.
        50 */
        51goog.define('goog.testing.jsunit.AUTO_RUN_ONLOAD', true);
        52
        53
        54/**
        55 * @define {number} Sets a delay in milliseconds after the window onload event
        56 * and running the tests. Used to prevent interference with Selenium and give
        57 * tests with asynchronous operations time to finish loading.
        58 */
        59goog.define('goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS', 500);
        60
        61
        62(function() {
        63 // Only allow one global test runner to be created on a page.
        64 if (goog.global['G_testRunner'] instanceof goog.testing.TestRunner) {
        65 return;
        66 }
        67
        68 // Increases the maximum number of stack frames in Google Chrome from the
        69 // default 10 to 50 to get more useful stack traces.
        70 Error.stackTraceLimit = 50;
        71
        72 // Store a reference to the window's timeout so that it can't be overridden
        73 // by tests.
        74 /** @type {!Function} */
        75 var realTimeout = window.setTimeout;
        76
        77 // Check for JsUnit's test runner (need to check for >2.2 and <=2.2)
        78 if (top['JsUnitTestManager'] || top['jsUnitTestManager']) {
        79 // Running inside JsUnit so add support code.
        80 var path = goog.basePath + goog.testing.jsunit.CORE_SCRIPT;
        81 document.write('<script type="text/javascript" src="' +
        82 path + '"></' + 'script>');
        83
        84 } else {
        85
        86 // Create a test runner.
        87 var tr = new goog.testing.TestRunner();
        88
        89 // Export it so that it can be queried by Selenium and tests that use a
        90 // compiled test runner.
        91 goog.exportSymbol('G_testRunner', tr);
        92 goog.exportSymbol('G_testRunner.initialize', tr.initialize);
        93 goog.exportSymbol('G_testRunner.isInitialized', tr.isInitialized);
        94 goog.exportSymbol('G_testRunner.isFinished', tr.isFinished);
        95 goog.exportSymbol('G_testRunner.isSuccess', tr.isSuccess);
        96 goog.exportSymbol('G_testRunner.getReport', tr.getReport);
        97 goog.exportSymbol('G_testRunner.getRunTime', tr.getRunTime);
        98 goog.exportSymbol('G_testRunner.getNumFilesLoaded', tr.getNumFilesLoaded);
        99 goog.exportSymbol('G_testRunner.setStrict', tr.setStrict);
        100 goog.exportSymbol('G_testRunner.logTestFailure', tr.logTestFailure);
        101 goog.exportSymbol('G_testRunner.getTestResults', tr.getTestResults);
        102
        103 // Export debug as a global function for JSUnit compatibility. This just
        104 // calls log on the current test case.
        105 if (!goog.global['debug']) {
        106 goog.exportSymbol('debug', goog.bind(tr.log, tr));
        107 }
        108
        109 // If the application has defined a global error filter, set it now. This
        110 // allows users who use a base test include to set the error filter before
        111 // the testing code is loaded.
        112 if (goog.global['G_errorFilter']) {
        113 tr.setErrorFilter(goog.global['G_errorFilter']);
        114 }
        115
        116 // Add an error handler to report errors that may occur during
        117 // initialization of the page.
        118 var onerror = window.onerror;
        119 window.onerror = function(error, url, line) {
        120 // Call any existing onerror handlers.
        121 if (onerror) {
        122 onerror(error, url, line);
        123 }
        124 if (typeof error == 'object') {
        125 // Webkit started passing an event object as the only argument to
        126 // window.onerror. It doesn't contain an error message, url or line
        127 // number. We therefore log as much info as we can.
        128 if (error.target && error.target.tagName == goog.dom.TagName.SCRIPT) {
        129 tr.logError('UNKNOWN ERROR: Script ' + error.target.src);
        130 } else {
        131 tr.logError('UNKNOWN ERROR: No error information available.');
        132 }
        133 } else {
        134 tr.logError('JS ERROR: ' + error + '\nURL: ' + url + '\nLine: ' + line);
        135 }
        136 };
        137
        138 // Create an onload handler, if the test runner hasn't been initialized then
        139 // no test has been registered with the test runner by the test file. We
        140 // then create a new test case and auto discover any tests in the global
        141 // scope. If this code is being parsed by JsTestC, we let it disable the
        142 // onload handler to avoid running the test in JsTestC.
        143 if (goog.testing.jsunit.AUTO_RUN_ONLOAD) {
        144 var onload = window.onload;
        145 window.onload = function(e) {
        146 // Call any existing onload handlers.
        147 if (onload) {
        148 onload(e);
        149 }
        150 // Wait so that we don't interfere with WebDriver.
        151 realTimeout(function() {
        152 if (!tr.initialized) {
        153 var testCase = new goog.testing.TestCase(document.title);
        154 goog.testing.TestCase.initializeTestRunner(testCase);
        155 }
        156 tr.execute();
        157 }, goog.testing.jsunit.AUTO_RUN_DELAY_IN_MS);
        158 window.onload = null;
        159 };
        160 }
        161 }
        162})();
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/loosemock.js.src.html b/docs/source/lib/goog/testing/loosemock.js.src.html new file mode 100644 index 0000000..1aacbe1 --- /dev/null +++ b/docs/source/lib/goog/testing/loosemock.js.src.html @@ -0,0 +1 @@ +loosemock.js

        lib/goog/testing/loosemock.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 This file defines a loose mock implementation.
        17 */
        18
        19goog.provide('goog.testing.LooseExpectationCollection');
        20goog.provide('goog.testing.LooseMock');
        21
        22goog.require('goog.array');
        23goog.require('goog.structs.Map');
        24goog.require('goog.testing.Mock');
        25
        26
        27
        28/**
        29 * This class is an ordered collection of expectations for one method. Since
        30 * the loose mock does most of its verification at the time of $verify, this
        31 * class is necessary to manage the return/throw behavior when the mock is
        32 * being called.
        33 * @constructor
        34 * @final
        35 */
        36goog.testing.LooseExpectationCollection = function() {
        37 /**
        38 * The list of expectations. All of these should have the same name.
        39 * @type {Array<goog.testing.MockExpectation>}
        40 * @private
        41 */
        42 this.expectations_ = [];
        43};
        44
        45
        46/**
        47 * Adds an expectation to this collection.
        48 * @param {goog.testing.MockExpectation} expectation The expectation to add.
        49 */
        50goog.testing.LooseExpectationCollection.prototype.addExpectation =
        51 function(expectation) {
        52 this.expectations_.push(expectation);
        53};
        54
        55
        56/**
        57 * Gets the list of expectations in this collection.
        58 * @return {Array<goog.testing.MockExpectation>} The array of expectations.
        59 */
        60goog.testing.LooseExpectationCollection.prototype.getExpectations = function() {
        61 return this.expectations_;
        62};
        63
        64
        65
        66/**
        67 * This is a mock that does not care about the order of method calls. As a
        68 * result, it won't throw exceptions until verify() is called. The only
        69 * exception is that if a method is called that has no expectations, then an
        70 * exception will be thrown.
        71 * @param {Object|Function} objectToMock The object that should be mocked, or
        72 * the constructor of an object to mock.
        73 * @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected
        74 * calls.
        75 * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
        76 * a mock should be constructed from the static functions of a class.
        77 * @param {boolean=} opt_createProxy An optional argument denoting that
        78 * a proxy for the target mock should be created.
        79 * @constructor
        80 * @extends {goog.testing.Mock}
        81 */
        82goog.testing.LooseMock = function(objectToMock, opt_ignoreUnexpectedCalls,
        83 opt_mockStaticMethods, opt_createProxy) {
        84 goog.testing.Mock.call(this, objectToMock, opt_mockStaticMethods,
        85 opt_createProxy);
        86
        87 /**
        88 * A map of method names to a LooseExpectationCollection for that method.
        89 * @type {goog.structs.Map}
        90 * @private
        91 */
        92 this.$expectations_ = new goog.structs.Map();
        93
        94 /**
        95 * The calls that have been made; we cache them to verify at the end. Each
        96 * element is an array where the first element is the name, and the second
        97 * element is the arguments.
        98 * @type {Array<Array<*>>}
        99 * @private
        100 */
        101 this.$calls_ = [];
        102
        103 /**
        104 * Whether to ignore unexpected calls.
        105 * @type {boolean}
        106 * @private
        107 */
        108 this.$ignoreUnexpectedCalls_ = !!opt_ignoreUnexpectedCalls;
        109};
        110goog.inherits(goog.testing.LooseMock, goog.testing.Mock);
        111
        112
        113/**
        114 * A setter for the ignoreUnexpectedCalls field.
        115 * @param {boolean} ignoreUnexpectedCalls Whether to ignore unexpected calls.
        116 * @return {!goog.testing.LooseMock} This mock object.
        117 */
        118goog.testing.LooseMock.prototype.$setIgnoreUnexpectedCalls = function(
        119 ignoreUnexpectedCalls) {
        120 this.$ignoreUnexpectedCalls_ = ignoreUnexpectedCalls;
        121 return this;
        122};
        123
        124
        125/** @override */
        126goog.testing.LooseMock.prototype.$recordExpectation = function() {
        127 if (!this.$expectations_.containsKey(this.$pendingExpectation.name)) {
        128 this.$expectations_.set(this.$pendingExpectation.name,
        129 new goog.testing.LooseExpectationCollection());
        130 }
        131
        132 var collection = this.$expectations_.get(this.$pendingExpectation.name);
        133 collection.addExpectation(this.$pendingExpectation);
        134};
        135
        136
        137/** @override */
        138goog.testing.LooseMock.prototype.$recordCall = function(name, args) {
        139 if (!this.$expectations_.containsKey(name)) {
        140 if (this.$ignoreUnexpectedCalls_) {
        141 return;
        142 }
        143 this.$throwCallException(name, args);
        144 }
        145
        146 // Start from the beginning of the expectations for this name,
        147 // and iterate over them until we find an expectation that matches
        148 // and also has calls remaining.
        149 var collection = this.$expectations_.get(name);
        150 var matchingExpectation = null;
        151 var expectations = collection.getExpectations();
        152 for (var i = 0; i < expectations.length; i++) {
        153 var expectation = expectations[i];
        154 if (this.$verifyCall(expectation, name, args)) {
        155 matchingExpectation = expectation;
        156 if (expectation.actualCalls < expectation.maxCalls) {
        157 break;
        158 } // else continue and see if we can find something that does match
        159 }
        160 }
        161 if (matchingExpectation == null) {
        162 this.$throwCallException(name, args, expectation);
        163 }
        164
        165 matchingExpectation.actualCalls++;
        166 if (matchingExpectation.actualCalls > matchingExpectation.maxCalls) {
        167 this.$throwException('Too many calls to ' + matchingExpectation.name +
        168 '\nExpected: ' + matchingExpectation.maxCalls + ' but was: ' +
        169 matchingExpectation.actualCalls);
        170 }
        171
        172 this.$calls_.push([name, args]);
        173 return this.$do(matchingExpectation, args);
        174};
        175
        176
        177/** @override */
        178goog.testing.LooseMock.prototype.$reset = function() {
        179 goog.testing.LooseMock.superClass_.$reset.call(this);
        180
        181 this.$expectations_ = new goog.structs.Map();
        182 this.$calls_ = [];
        183};
        184
        185
        186/** @override */
        187goog.testing.LooseMock.prototype.$replay = function() {
        188 goog.testing.LooseMock.superClass_.$replay.call(this);
        189
        190 // Verify that there are no expectations that can never be reached.
        191 // This can't catch every situation, but it is a decent sanity check
        192 // and it's similar to the behavior of EasyMock in java.
        193 var collections = this.$expectations_.getValues();
        194 for (var i = 0; i < collections.length; i++) {
        195 var expectations = collections[i].getExpectations();
        196 for (var j = 0; j < expectations.length; j++) {
        197 var expectation = expectations[j];
        198 // If this expectation can be called infinite times, then
        199 // check if any subsequent expectation has the exact same
        200 // argument list.
        201 if (!isFinite(expectation.maxCalls)) {
        202 for (var k = j + 1; k < expectations.length; k++) {
        203 var laterExpectation = expectations[k];
        204 if (laterExpectation.minCalls > 0 &&
        205 goog.array.equals(expectation.argumentList,
        206 laterExpectation.argumentList)) {
        207 var name = expectation.name;
        208 var argsString = this.$argumentsAsString(expectation.argumentList);
        209 this.$throwException([
        210 'Expected call to ', name, ' with arguments ', argsString,
        211 ' has an infinite max number of calls; can\'t expect an',
        212 ' identical call later with a positive min number of calls'
        213 ].join(''));
        214 }
        215 }
        216 }
        217 }
        218 }
        219};
        220
        221
        222/** @override */
        223goog.testing.LooseMock.prototype.$verify = function() {
        224 goog.testing.LooseMock.superClass_.$verify.call(this);
        225 var collections = this.$expectations_.getValues();
        226
        227 for (var i = 0; i < collections.length; i++) {
        228 var expectations = collections[i].getExpectations();
        229 for (var j = 0; j < expectations.length; j++) {
        230 var expectation = expectations[j];
        231 if (expectation.actualCalls > expectation.maxCalls) {
        232 this.$throwException('Too many calls to ' + expectation.name +
        233 '\nExpected: ' + expectation.maxCalls + ' but was: ' +
        234 expectation.actualCalls);
        235 } else if (expectation.actualCalls < expectation.minCalls) {
        236 this.$throwException('Not enough calls to ' + expectation.name +
        237 '\nExpected: ' + expectation.minCalls + ' but was: ' +
        238 expectation.actualCalls);
        239 }
        240 }
        241 }
        242};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/mock.js.src.html b/docs/source/lib/goog/testing/mock.js.src.html new file mode 100644 index 0000000..6793480 --- /dev/null +++ b/docs/source/lib/goog/testing/mock.js.src.html @@ -0,0 +1 @@ +mock.js

        lib/goog/testing/mock.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 This file defines base classes used for creating mocks in
        17 * JavaScript. The API was inspired by EasyMock.
        18 *
        19 * The basic API is:
        20 * <ul>
        21 * <li>Create an object to be mocked
        22 * <li>Create a mock object, passing in the above object to the constructor
        23 * <li>Set expectations by calling methods on the mock object
        24 * <li>Call $replay() on the mock object
        25 * <li>Pass the mock to code that will make real calls on it
        26 * <li>Call $verify() to make sure that expectations were met
        27 * </ul>
        28 *
        29 * For examples, please see the unit tests for LooseMock and StrictMock.
        30 *
        31 * Still TODO
        32 * implement better (and pluggable) argument matching
        33 * Have the exceptions for LooseMock show the number of expected/actual calls
        34 * loose and strict mocks share a lot of code - move it to the base class
        35 *
        36 */
        37
        38goog.provide('goog.testing.Mock');
        39goog.provide('goog.testing.MockExpectation');
        40
        41goog.require('goog.array');
        42goog.require('goog.object');
        43goog.require('goog.testing.JsUnitException');
        44goog.require('goog.testing.MockInterface');
        45goog.require('goog.testing.mockmatchers');
        46
        47
        48
        49/**
        50 * This is a class that represents an expectation.
        51 * @param {string} name The name of the method for this expectation.
        52 * @constructor
        53 * @final
        54 */
        55goog.testing.MockExpectation = function(name) {
        56 /**
        57 * The name of the method that is expected to be called.
        58 * @type {string}
        59 */
        60 this.name = name;
        61
        62 /**
        63 * An array of error messages for expectations not met.
        64 * @type {Array<string>}
        65 */
        66 this.errorMessages = [];
        67};
        68
        69
        70/**
        71 * The minimum number of times this method should be called.
        72 * @type {number}
        73 */
        74goog.testing.MockExpectation.prototype.minCalls = 1;
        75
        76
        77/**
        78 * The maximum number of times this method should be called.
        79 * @type {number}
        80 */
        81goog.testing.MockExpectation.prototype.maxCalls = 1;
        82
        83
        84/**
        85 * The value that this method should return.
        86 * @type {*}
        87 */
        88goog.testing.MockExpectation.prototype.returnValue;
        89
        90
        91/**
        92 * The value that will be thrown when the method is called
        93 * @type {*}
        94 */
        95goog.testing.MockExpectation.prototype.exceptionToThrow;
        96
        97
        98/**
        99 * The arguments that are expected to be passed to this function
        100 * @type {Array<*>}
        101 */
        102goog.testing.MockExpectation.prototype.argumentList;
        103
        104
        105/**
        106 * The number of times this method is called by real code.
        107 * @type {number}
        108 */
        109goog.testing.MockExpectation.prototype.actualCalls = 0;
        110
        111
        112/**
        113 * The number of times this method is called during the verification phase.
        114 * @type {number}
        115 */
        116goog.testing.MockExpectation.prototype.verificationCalls = 0;
        117
        118
        119/**
        120 * The function which will be executed when this method is called.
        121 * Method arguments will be passed to this function, and return value
        122 * of this function will be returned by the method.
        123 * @type {Function}
        124 */
        125goog.testing.MockExpectation.prototype.toDo;
        126
        127
        128/**
        129 * Allow expectation failures to include messages.
        130 * @param {string} message The failure message.
        131 */
        132goog.testing.MockExpectation.prototype.addErrorMessage = function(message) {
        133 this.errorMessages.push(message);
        134};
        135
        136
        137/**
        138 * Get the error messages seen so far.
        139 * @return {string} Error messages separated by \n.
        140 */
        141goog.testing.MockExpectation.prototype.getErrorMessage = function() {
        142 return this.errorMessages.join('\n');
        143};
        144
        145
        146/**
        147 * Get how many error messages have been seen so far.
        148 * @return {number} Count of error messages.
        149 */
        150goog.testing.MockExpectation.prototype.getErrorMessageCount = function() {
        151 return this.errorMessages.length;
        152};
        153
        154
        155
        156/**
        157 * The base class for a mock object.
        158 * @param {Object|Function} objectToMock The object that should be mocked, or
        159 * the constructor of an object to mock.
        160 * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
        161 * a mock should be constructed from the static functions of a class.
        162 * @param {boolean=} opt_createProxy An optional argument denoting that
        163 * a proxy for the target mock should be created.
        164 * @constructor
        165 * @implements {goog.testing.MockInterface}
        166 */
        167goog.testing.Mock = function(objectToMock, opt_mockStaticMethods,
        168 opt_createProxy) {
        169 if (!goog.isObject(objectToMock) && !goog.isFunction(objectToMock)) {
        170 throw new Error('objectToMock must be an object or constructor.');
        171 }
        172 if (opt_createProxy && !opt_mockStaticMethods &&
        173 goog.isFunction(objectToMock)) {
        174 /**
        175 * @constructor
        176 * @final
        177 */
        178 var tempCtor = function() {};
        179 goog.inherits(tempCtor, objectToMock);
        180 this.$proxy = new tempCtor();
        181 } else if (opt_createProxy && opt_mockStaticMethods &&
        182 goog.isFunction(objectToMock)) {
        183 throw Error('Cannot create a proxy when opt_mockStaticMethods is true');
        184 } else if (opt_createProxy && !goog.isFunction(objectToMock)) {
        185 throw Error('Must have a constructor to create a proxy');
        186 }
        187
        188 if (goog.isFunction(objectToMock) && !opt_mockStaticMethods) {
        189 this.$initializeFunctions_(objectToMock.prototype);
        190 } else {
        191 this.$initializeFunctions_(objectToMock);
        192 }
        193 this.$argumentListVerifiers_ = {};
        194};
        195
        196
        197/**
        198 * Option that may be passed when constructing function, method, and
        199 * constructor mocks. Indicates that the expected calls should be accepted in
        200 * any order.
        201 * @const
        202 * @type {number}
        203 */
        204goog.testing.Mock.LOOSE = 1;
        205
        206
        207/**
        208 * Option that may be passed when constructing function, method, and
        209 * constructor mocks. Indicates that the expected calls should be accepted in
        210 * the recorded order only.
        211 * @const
        212 * @type {number}
        213 */
        214goog.testing.Mock.STRICT = 0;
        215
        216
        217/**
        218 * This array contains the name of the functions that are part of the base
        219 * Object prototype.
        220 * Basically a copy of goog.object.PROTOTYPE_FIELDS_.
        221 * @const
        222 * @type {!Array<string>}
        223 * @private
        224 */
        225goog.testing.Mock.PROTOTYPE_FIELDS_ = [
        226 'constructor',
        227 'hasOwnProperty',
        228 'isPrototypeOf',
        229 'propertyIsEnumerable',
        230 'toLocaleString',
        231 'toString',
        232 'valueOf'
        233];
        234
        235
        236/**
        237 * A proxy for the mock. This can be used for dependency injection in lieu of
        238 * the mock if the test requires a strict instanceof check.
        239 * @type {Object}
        240 */
        241goog.testing.Mock.prototype.$proxy = null;
        242
        243
        244/**
        245 * Map of argument name to optional argument list verifier function.
        246 * @type {Object}
        247 */
        248goog.testing.Mock.prototype.$argumentListVerifiers_;
        249
        250
        251/**
        252 * Whether or not we are in recording mode.
        253 * @type {boolean}
        254 * @private
        255 */
        256goog.testing.Mock.prototype.$recording_ = true;
        257
        258
        259/**
        260 * The expectation currently being created. All methods that modify the
        261 * current expectation return the Mock object for easy chaining, so this is
        262 * where we keep track of the expectation that's currently being modified.
        263 * @type {goog.testing.MockExpectation}
        264 * @protected
        265 */
        266goog.testing.Mock.prototype.$pendingExpectation;
        267
        268
        269/**
        270 * First exception thrown by this mock; used in $verify.
        271 * @type {Object}
        272 * @private
        273 */
        274goog.testing.Mock.prototype.$threwException_ = null;
        275
        276
        277/**
        278 * Initializes the functions on the mock object.
        279 * @param {Object} objectToMock The object being mocked.
        280 * @private
        281 */
        282goog.testing.Mock.prototype.$initializeFunctions_ = function(objectToMock) {
        283 // Gets the object properties.
        284 var enumerableProperties = goog.object.getKeys(objectToMock);
        285
        286 // The non enumerable properties are added if they override the ones in the
        287 // Object prototype. This is due to the fact that IE8 does not enumerate any
        288 // of the prototype Object functions even when overriden and mocking these is
        289 // sometimes needed.
        290 for (var i = 0; i < goog.testing.Mock.PROTOTYPE_FIELDS_.length; i++) {
        291 var prop = goog.testing.Mock.PROTOTYPE_FIELDS_[i];
        292 // Look at b/6758711 if you're considering adding ALL properties to ALL
        293 // mocks.
        294 if (objectToMock[prop] !== Object.prototype[prop]) {
        295 enumerableProperties.push(prop);
        296 }
        297 }
        298
        299 // Adds the properties to the mock.
        300 for (var i = 0; i < enumerableProperties.length; i++) {
        301 var prop = enumerableProperties[i];
        302 if (typeof objectToMock[prop] == 'function') {
        303 this[prop] = goog.bind(this.$mockMethod, this, prop);
        304 if (this.$proxy) {
        305 this.$proxy[prop] = goog.bind(this.$mockMethod, this, prop);
        306 }
        307 }
        308 }
        309};
        310
        311
        312/**
        313 * Registers a verifier function to use when verifying method argument lists.
        314 * @param {string} methodName The name of the method for which the verifierFn
        315 * should be used.
        316 * @param {Function} fn Argument list verifier function. Should take 2 argument
        317 * arrays as arguments, and return true if they are considered equivalent.
        318 * @return {!goog.testing.Mock} This mock object.
        319 */
        320goog.testing.Mock.prototype.$registerArgumentListVerifier = function(methodName,
        321 fn) {
        322 this.$argumentListVerifiers_[methodName] = fn;
        323 return this;
        324};
        325
        326
        327/**
        328 * The function that replaces all methods on the mock object.
        329 * @param {string} name The name of the method being mocked.
        330 * @return {*} In record mode, returns the mock object. In replay mode, returns
        331 * whatever the creator of the mock set as the return value.
        332 */
        333goog.testing.Mock.prototype.$mockMethod = function(name) {
        334 try {
        335 // Shift off the name argument so that args contains the arguments to
        336 // the mocked method.
        337 var args = goog.array.slice(arguments, 1);
        338 if (this.$recording_) {
        339 this.$pendingExpectation = new goog.testing.MockExpectation(name);
        340 this.$pendingExpectation.argumentList = args;
        341 this.$recordExpectation();
        342 return this;
        343 } else {
        344 return this.$recordCall(name, args);
        345 }
        346 } catch (ex) {
        347 this.$recordAndThrow(ex);
        348 }
        349};
        350
        351
        352/**
        353 * Records the currently pending expectation, intended to be overridden by a
        354 * subclass.
        355 * @protected
        356 */
        357goog.testing.Mock.prototype.$recordExpectation = function() {};
        358
        359
        360/**
        361 * Records an actual method call, intended to be overridden by a
        362 * subclass. The subclass must find the pending expectation and return the
        363 * correct value.
        364 * @param {string} name The name of the method being called.
        365 * @param {Array<?>} args The arguments to the method.
        366 * @return {*} The return expected by the mock.
        367 * @protected
        368 */
        369goog.testing.Mock.prototype.$recordCall = function(name, args) {
        370 return undefined;
        371};
        372
        373
        374/**
        375 * If the expectation expects to throw, this method will throw.
        376 * @param {goog.testing.MockExpectation} expectation The expectation.
        377 */
        378goog.testing.Mock.prototype.$maybeThrow = function(expectation) {
        379 if (typeof expectation.exceptionToThrow != 'undefined') {
        380 throw expectation.exceptionToThrow;
        381 }
        382};
        383
        384
        385/**
        386 * If this expectation defines a function to be called,
        387 * it will be called and its result will be returned.
        388 * Otherwise, if the expectation expects to throw, it will throw.
        389 * Otherwise, this method will return defined value.
        390 * @param {goog.testing.MockExpectation} expectation The expectation.
        391 * @param {Array<?>} args The arguments to the method.
        392 * @return {*} The return value expected by the mock.
        393 */
        394goog.testing.Mock.prototype.$do = function(expectation, args) {
        395 if (typeof expectation.toDo == 'undefined') {
        396 this.$maybeThrow(expectation);
        397 return expectation.returnValue;
        398 } else {
        399 return expectation.toDo.apply(this, args);
        400 }
        401};
        402
        403
        404/**
        405 * Specifies a return value for the currently pending expectation.
        406 * @param {*} val The return value.
        407 * @return {!goog.testing.Mock} This mock object.
        408 */
        409goog.testing.Mock.prototype.$returns = function(val) {
        410 this.$pendingExpectation.returnValue = val;
        411 return this;
        412};
        413
        414
        415/**
        416 * Specifies a value for the currently pending expectation to throw.
        417 * @param {*} val The value to throw.
        418 * @return {!goog.testing.Mock} This mock object.
        419 */
        420goog.testing.Mock.prototype.$throws = function(val) {
        421 this.$pendingExpectation.exceptionToThrow = val;
        422 return this;
        423};
        424
        425
        426/**
        427 * Specifies a function to call for currently pending expectation.
        428 * Note, that using this method overrides declarations made
        429 * using $returns() and $throws() methods.
        430 * @param {Function} func The function to call.
        431 * @return {!goog.testing.Mock} This mock object.
        432 */
        433goog.testing.Mock.prototype.$does = function(func) {
        434 this.$pendingExpectation.toDo = func;
        435 return this;
        436};
        437
        438
        439/**
        440 * Allows the expectation to be called 0 or 1 times.
        441 * @return {!goog.testing.Mock} This mock object.
        442 */
        443goog.testing.Mock.prototype.$atMostOnce = function() {
        444 this.$pendingExpectation.minCalls = 0;
        445 this.$pendingExpectation.maxCalls = 1;
        446 return this;
        447};
        448
        449
        450/**
        451 * Allows the expectation to be called any number of times, as long as it's
        452 * called once.
        453 * @return {!goog.testing.Mock} This mock object.
        454 */
        455goog.testing.Mock.prototype.$atLeastOnce = function() {
        456 this.$pendingExpectation.maxCalls = Infinity;
        457 return this;
        458};
        459
        460
        461/**
        462 * Allows the expectation to be called exactly once.
        463 * @return {!goog.testing.Mock} This mock object.
        464 */
        465goog.testing.Mock.prototype.$once = function() {
        466 this.$pendingExpectation.minCalls = 1;
        467 this.$pendingExpectation.maxCalls = 1;
        468 return this;
        469};
        470
        471
        472/**
        473 * Disallows the expectation from being called.
        474 * @return {!goog.testing.Mock} This mock object.
        475 */
        476goog.testing.Mock.prototype.$never = function() {
        477 this.$pendingExpectation.minCalls = 0;
        478 this.$pendingExpectation.maxCalls = 0;
        479 return this;
        480};
        481
        482
        483/**
        484 * Allows the expectation to be called any number of times.
        485 * @return {!goog.testing.Mock} This mock object.
        486 */
        487goog.testing.Mock.prototype.$anyTimes = function() {
        488 this.$pendingExpectation.minCalls = 0;
        489 this.$pendingExpectation.maxCalls = Infinity;
        490 return this;
        491};
        492
        493
        494/**
        495 * Specifies the number of times the expectation should be called.
        496 * @param {number} times The number of times this method will be called.
        497 * @return {!goog.testing.Mock} This mock object.
        498 */
        499goog.testing.Mock.prototype.$times = function(times) {
        500 this.$pendingExpectation.minCalls = times;
        501 this.$pendingExpectation.maxCalls = times;
        502 return this;
        503};
        504
        505
        506/**
        507 * Switches from recording to replay mode.
        508 * @override
        509 */
        510goog.testing.Mock.prototype.$replay = function() {
        511 this.$recording_ = false;
        512};
        513
        514
        515/**
        516 * Resets the state of this mock object. This clears all pending expectations
        517 * without verifying, and puts the mock in recording mode.
        518 * @override
        519 */
        520goog.testing.Mock.prototype.$reset = function() {
        521 this.$recording_ = true;
        522 this.$threwException_ = null;
        523 delete this.$pendingExpectation;
        524};
        525
        526
        527/**
        528 * Throws an exception and records that an exception was thrown.
        529 * @param {string} comment A short comment about the exception.
        530 * @param {?string=} opt_message A longer message about the exception.
        531 * @throws {Object} JsUnitException object.
        532 * @protected
        533 */
        534goog.testing.Mock.prototype.$throwException = function(comment, opt_message) {
        535 this.$recordAndThrow(new goog.testing.JsUnitException(comment, opt_message));
        536};
        537
        538
        539/**
        540 * Throws an exception and records that an exception was thrown.
        541 * @param {Object} ex Exception.
        542 * @throws {Object} #ex.
        543 * @protected
        544 */
        545goog.testing.Mock.prototype.$recordAndThrow = function(ex) {
        546 // If it's an assert exception, record it.
        547 if (ex['isJsUnitException']) {
        548 var testRunner = goog.global['G_testRunner'];
        549 if (testRunner) {
        550 var logTestFailureFunction = testRunner['logTestFailure'];
        551 if (logTestFailureFunction) {
        552 logTestFailureFunction.call(testRunner, ex);
        553 }
        554 }
        555
        556 if (!this.$threwException_) {
        557 // Only remember first exception thrown.
        558 this.$threwException_ = ex;
        559 }
        560 }
        561 throw ex;
        562};
        563
        564
        565/**
        566 * Verify that all of the expectations were met. Should be overridden by
        567 * subclasses.
        568 * @override
        569 */
        570goog.testing.Mock.prototype.$verify = function() {
        571 if (this.$threwException_) {
        572 throw this.$threwException_;
        573 }
        574};
        575
        576
        577/**
        578 * Verifies that a method call matches an expectation.
        579 * @param {goog.testing.MockExpectation} expectation The expectation to check.
        580 * @param {string} name The name of the called method.
        581 * @param {Array<*>?} args The arguments passed to the mock.
        582 * @return {boolean} Whether the call matches the expectation.
        583 */
        584goog.testing.Mock.prototype.$verifyCall = function(expectation, name, args) {
        585 if (expectation.name != name) {
        586 return false;
        587 }
        588 var verifierFn =
        589 this.$argumentListVerifiers_.hasOwnProperty(expectation.name) ?
        590 this.$argumentListVerifiers_[expectation.name] :
        591 goog.testing.mockmatchers.flexibleArrayMatcher;
        592
        593 return verifierFn(expectation.argumentList, args, expectation);
        594};
        595
        596
        597/**
        598 * Render the provided argument array to a string to help
        599 * clients with debugging tests.
        600 * @param {Array<*>?} args The arguments passed to the mock.
        601 * @return {string} Human-readable string.
        602 */
        603goog.testing.Mock.prototype.$argumentsAsString = function(args) {
        604 var retVal = [];
        605 for (var i = 0; i < args.length; i++) {
        606 try {
        607 retVal.push(goog.typeOf(args[i]));
        608 } catch (e) {
        609 retVal.push('[unknown]');
        610 }
        611 }
        612 return '(' + retVal.join(', ') + ')';
        613};
        614
        615
        616/**
        617 * Throw an exception based on an incorrect method call.
        618 * @param {string} name Name of method called.
        619 * @param {Array<*>?} args Arguments passed to the mock.
        620 * @param {goog.testing.MockExpectation=} opt_expectation Expected next call,
        621 * if any.
        622 */
        623goog.testing.Mock.prototype.$throwCallException = function(name, args,
        624 opt_expectation) {
        625 var errorStringBuffer = [];
        626 var actualArgsString = this.$argumentsAsString(args);
        627 var expectedArgsString = opt_expectation ?
        628 this.$argumentsAsString(opt_expectation.argumentList) : '';
        629
        630 if (opt_expectation && opt_expectation.name == name) {
        631 errorStringBuffer.push('Bad arguments to ', name, '().\n',
        632 'Actual: ', actualArgsString, '\n',
        633 'Expected: ', expectedArgsString, '\n',
        634 opt_expectation.getErrorMessage());
        635 } else {
        636 errorStringBuffer.push('Unexpected call to ', name,
        637 actualArgsString, '.');
        638 if (opt_expectation) {
        639 errorStringBuffer.push('\nNext expected call was to ',
        640 opt_expectation.name,
        641 expectedArgsString);
        642 }
        643 }
        644 this.$throwException(errorStringBuffer.join(''));
        645};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/mockclock.js.src.html b/docs/source/lib/goog/testing/mockclock.js.src.html new file mode 100644 index 0000000..d353de5 --- /dev/null +++ b/docs/source/lib/goog/testing/mockclock.js.src.html @@ -0,0 +1 @@ +mockclock.js

        lib/goog/testing/mockclock.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 Mock Clock implementation for working with setTimeout,
        17 * setInterval, clearTimeout and clearInterval within unit tests.
        18 *
        19 * Derived from jsUnitMockTimeout.js, contributed to JsUnit by
        20 * Pivotal Computer Systems, www.pivotalsf.com
        21 *
        22 */
        23
        24goog.provide('goog.testing.MockClock');
        25
        26goog.require('goog.Disposable');
        27goog.require('goog.async.run');
        28goog.require('goog.testing.PropertyReplacer');
        29goog.require('goog.testing.events');
        30goog.require('goog.testing.events.Event');
        31
        32
        33
        34/**
        35 * Class for unit testing code that uses setTimeout and clearTimeout.
        36 *
        37 * NOTE: If you are using MockClock to test code that makes use of
        38 * goog.fx.Animation, then you must either:
        39 *
        40 * 1. Install and dispose of the MockClock in setUpPage() and tearDownPage()
        41 * respectively (rather than setUp()/tearDown()).
        42 *
        43 * or
        44 *
        45 * 2. Ensure that every test clears the animation queue by calling
        46 * mockClock.tick(x) at the end of each test function (where `x` is large
        47 * enough to complete all animations).
        48 *
        49 * Otherwise, if any animation is left pending at the time that
        50 * MockClock.dispose() is called, that will permanently prevent any future
        51 * animations from playing on the page.
        52 *
        53 * @param {boolean=} opt_autoInstall Install the MockClock at construction time.
        54 * @constructor
        55 * @extends {goog.Disposable}
        56 * @final
        57 */
        58goog.testing.MockClock = function(opt_autoInstall) {
        59 goog.Disposable.call(this);
        60
        61 /**
        62 * Reverse-order queue of timers to fire.
        63 *
        64 * The last item of the queue is popped off. Insertion happens from the
        65 * right. For example, the expiration times for each element of the queue
        66 * might be in the order 300, 200, 200.
        67 *
        68 * @type {Array<Object>}
        69 * @private
        70 */
        71 this.queue_ = [];
        72
        73 /**
        74 * Set of timeouts that should be treated as cancelled.
        75 *
        76 * Rather than removing cancelled timers directly from the queue, this set
        77 * simply marks them as deleted so that they can be ignored when their
        78 * turn comes up. The keys are the timeout keys that are cancelled, each
        79 * mapping to true.
        80 *
        81 * @private {Object<number, boolean>}
        82 */
        83 this.deletedKeys_ = {};
        84
        85 if (opt_autoInstall) {
        86 this.install();
        87 }
        88};
        89goog.inherits(goog.testing.MockClock, goog.Disposable);
        90
        91
        92/**
        93 * Default wait timeout for mocking requestAnimationFrame (in milliseconds).
        94 *
        95 * @type {number}
        96 * @const
        97 */
        98goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT = 20;
        99
        100
        101/**
        102 * ID to use for next timeout. Timeout IDs must never be reused, even across
        103 * MockClock instances.
        104 * @public {number}
        105 */
        106goog.testing.MockClock.nextId = Math.round(Math.random() * 10000);
        107
        108
        109/**
        110 * Count of the number of timeouts made by this instance.
        111 * @type {number}
        112 * @private
        113 */
        114goog.testing.MockClock.prototype.timeoutsMade_ = 0;
        115
        116
        117/**
        118 * PropertyReplacer instance which overwrites and resets setTimeout,
        119 * setInterval, etc. or null if the MockClock is not installed.
        120 * @type {goog.testing.PropertyReplacer}
        121 * @private
        122 */
        123goog.testing.MockClock.prototype.replacer_ = null;
        124
        125
        126/**
        127 * The current simulated time in milliseconds.
        128 * @type {number}
        129 * @private
        130 */
        131goog.testing.MockClock.prototype.nowMillis_ = 0;
        132
        133
        134/**
        135 * Additional delay between the time a timeout was set to fire, and the time
        136 * it actually fires. Useful for testing workarounds for this Firefox 2 bug:
        137 * https://bugzilla.mozilla.org/show_bug.cgi?id=291386
        138 * May be negative.
        139 * @type {number}
        140 * @private
        141 */
        142goog.testing.MockClock.prototype.timeoutDelay_ = 0;
        143
        144
        145/**
        146 * The real set timeout for reference.
        147 * @const @private {!Function}
        148 */
        149goog.testing.MockClock.REAL_SETTIMEOUT_ = goog.global.setTimeout;
        150
        151
        152/**
        153 * Installs the MockClock by overriding the global object's implementation of
        154 * setTimeout, setInterval, clearTimeout and clearInterval.
        155 */
        156goog.testing.MockClock.prototype.install = function() {
        157 if (!this.replacer_) {
        158 if (goog.testing.MockClock.REAL_SETTIMEOUT_ !== goog.global.setTimeout) {
        159 if (typeof console !== 'undefined' && console.warn) {
        160 console.warn('Non default setTimeout detected. ' +
        161 'Use of multiple MockClock instances or other clock mocking ' +
        162 'should be avoided due to unspecified behavior and ' +
        163 'the resulting fragility.');
        164 }
        165 }
        166
        167 var r = this.replacer_ = new goog.testing.PropertyReplacer();
        168 r.set(goog.global, 'setTimeout', goog.bind(this.setTimeout_, this));
        169 r.set(goog.global, 'setInterval', goog.bind(this.setInterval_, this));
        170 r.set(goog.global, 'setImmediate', goog.bind(this.setImmediate_, this));
        171 r.set(goog.global, 'clearTimeout', goog.bind(this.clearTimeout_, this));
        172 r.set(goog.global, 'clearInterval', goog.bind(this.clearInterval_, this));
        173 // goog.Promise uses goog.async.run. In order to be able to test
        174 // Promise-based code, we need to make sure that goog.async.run uses
        175 // nextTick instead of native browser Promises. This means that it will
        176 // default to setImmediate, which is replaced above. Note that we test for
        177 // the presence of goog.async.run.forceNextTick to be resilient to the case
        178 // where tests replace goog.async.run directly.
        179 goog.async.run.forceNextTick && goog.async.run.forceNextTick(
        180 goog.testing.MockClock.REAL_SETTIMEOUT_);
        181
        182 // Replace the requestAnimationFrame functions.
        183 this.replaceRequestAnimationFrame_();
        184
        185 // PropertyReplacer#set can't be called with renameable functions.
        186 this.oldGoogNow_ = goog.now;
        187 goog.now = goog.bind(this.getCurrentTime, this);
        188 }
        189};
        190
        191
        192/**
        193 * Installs the mocks for requestAnimationFrame and cancelRequestAnimationFrame.
        194 * @private
        195 */
        196goog.testing.MockClock.prototype.replaceRequestAnimationFrame_ = function() {
        197 var r = this.replacer_;
        198 var requestFuncs = ['requestAnimationFrame',
        199 'webkitRequestAnimationFrame',
        200 'mozRequestAnimationFrame',
        201 'oRequestAnimationFrame',
        202 'msRequestAnimationFrame'];
        203
        204 var cancelFuncs = ['cancelAnimationFrame',
        205 'cancelRequestAnimationFrame',
        206 'webkitCancelRequestAnimationFrame',
        207 'mozCancelRequestAnimationFrame',
        208 'oCancelRequestAnimationFrame',
        209 'msCancelRequestAnimationFrame'];
        210
        211 for (var i = 0; i < requestFuncs.length; ++i) {
        212 if (goog.global && goog.global[requestFuncs[i]]) {
        213 r.set(goog.global, requestFuncs[i],
        214 goog.bind(this.requestAnimationFrame_, this));
        215 }
        216 }
        217
        218 for (var i = 0; i < cancelFuncs.length; ++i) {
        219 if (goog.global && goog.global[cancelFuncs[i]]) {
        220 r.set(goog.global, cancelFuncs[i],
        221 goog.bind(this.cancelRequestAnimationFrame_, this));
        222 }
        223 }
        224};
        225
        226
        227/**
        228 * Removes the MockClock's hooks into the global object's functions and revert
        229 * to their original values.
        230 */
        231goog.testing.MockClock.prototype.uninstall = function() {
        232 if (this.replacer_) {
        233 this.replacer_.reset();
        234 this.replacer_ = null;
        235 goog.now = this.oldGoogNow_;
        236 }
        237
        238 this.resetAsyncQueue_();
        239};
        240
        241
        242/** @override */
        243goog.testing.MockClock.prototype.disposeInternal = function() {
        244 this.uninstall();
        245 this.queue_ = null;
        246 this.deletedKeys_ = null;
        247 goog.testing.MockClock.superClass_.disposeInternal.call(this);
        248};
        249
        250
        251/**
        252 * Resets the MockClock, removing all timeouts that are scheduled and resets
        253 * the fake timer count.
        254 */
        255goog.testing.MockClock.prototype.reset = function() {
        256 this.queue_ = [];
        257 this.deletedKeys_ = {};
        258 this.nowMillis_ = 0;
        259 this.timeoutsMade_ = 0;
        260 this.timeoutDelay_ = 0;
        261
        262 this.resetAsyncQueue_();
        263};
        264
        265
        266/**
        267 * Resets the async queue when this clock resets.
        268 * @private
        269 */
        270goog.testing.MockClock.prototype.resetAsyncQueue_ = function() {
        271 goog.async.run.resetQueue();
        272};
        273
        274
        275/**
        276 * Sets the amount of time between when a timeout is scheduled to fire and when
        277 * it actually fires.
        278 * @param {number} delay The delay in milliseconds. May be negative.
        279 */
        280goog.testing.MockClock.prototype.setTimeoutDelay = function(delay) {
        281 this.timeoutDelay_ = delay;
        282};
        283
        284
        285/**
        286 * @return {number} delay The amount of time between when a timeout is
        287 * scheduled to fire and when it actually fires, in milliseconds. May
        288 * be negative.
        289 */
        290goog.testing.MockClock.prototype.getTimeoutDelay = function() {
        291 return this.timeoutDelay_;
        292};
        293
        294
        295/**
        296 * Increments the MockClock's time by a given number of milliseconds, running
        297 * any functions that are now overdue.
        298 * @param {number=} opt_millis Number of milliseconds to increment the counter.
        299 * If not specified, clock ticks 1 millisecond.
        300 * @return {number} Current mock time in milliseconds.
        301 */
        302goog.testing.MockClock.prototype.tick = function(opt_millis) {
        303 if (typeof opt_millis != 'number') {
        304 opt_millis = 1;
        305 }
        306 var endTime = this.nowMillis_ + opt_millis;
        307 this.runFunctionsWithinRange_(endTime);
        308 this.nowMillis_ = endTime;
        309 return endTime;
        310};
        311
        312
        313/**
        314 * Takes a promise and then ticks the mock clock. If the promise successfully
        315 * resolves, returns the value produced by the promise. If the promise is
        316 * rejected, it throws the rejection as an exception. If the promise is not
        317 * resolved at all, throws an exception.
        318 * Also ticks the general clock by the specified amount.
        319 *
        320 * @param {!goog.Thenable<T>} promise A promise that should be resolved after
        321 * the mockClock is ticked for the given opt_millis.
        322 * @param {number=} opt_millis Number of milliseconds to increment the counter.
        323 * If not specified, clock ticks 1 millisecond.
        324 * @return {T}
        325 * @template T
        326 */
        327goog.testing.MockClock.prototype.tickPromise = function(promise, opt_millis) {
        328 var value;
        329 var error;
        330 var resolved = false;
        331 promise.then(function(v) {
        332 value = v;
        333 resolved = true;
        334 }, function(e) {
        335 error = e;
        336 resolved = true;
        337 });
        338 this.tick(opt_millis);
        339 if (!resolved) {
        340 throw new Error(
        341 'Promise was expected to be resolved after mock clock tick.');
        342 }
        343 if (error) {
        344 throw error;
        345 }
        346 return value;
        347};
        348
        349
        350/**
        351 * @return {number} The number of timeouts that have been scheduled.
        352 */
        353goog.testing.MockClock.prototype.getTimeoutsMade = function() {
        354 return this.timeoutsMade_;
        355};
        356
        357
        358/**
        359 * @return {number} The MockClock's current time in milliseconds.
        360 */
        361goog.testing.MockClock.prototype.getCurrentTime = function() {
        362 return this.nowMillis_;
        363};
        364
        365
        366/**
        367 * @param {number} timeoutKey The timeout key.
        368 * @return {boolean} Whether the timer has been set and not cleared,
        369 * independent of the timeout's expiration. In other words, the timeout
        370 * could have passed or could be scheduled for the future. Either way,
        371 * this function returns true or false depending only on whether the
        372 * provided timeoutKey represents a timeout that has been set and not
        373 * cleared.
        374 */
        375goog.testing.MockClock.prototype.isTimeoutSet = function(timeoutKey) {
        376 return timeoutKey < goog.testing.MockClock.nextId &&
        377 timeoutKey >= goog.testing.MockClock.nextId - this.timeoutsMade_ &&
        378 !this.deletedKeys_[timeoutKey];
        379};
        380
        381
        382/**
        383 * Runs any function that is scheduled before a certain time. Timeouts can
        384 * be made to fire early or late if timeoutDelay_ is non-0.
        385 * @param {number} endTime The latest time in the range, in milliseconds.
        386 * @private
        387 */
        388goog.testing.MockClock.prototype.runFunctionsWithinRange_ = function(
        389 endTime) {
        390 var adjustedEndTime = endTime - this.timeoutDelay_;
        391
        392 // Repeatedly pop off the last item since the queue is always sorted.
        393 while (this.queue_ && this.queue_.length &&
        394 this.queue_[this.queue_.length - 1].runAtMillis <= adjustedEndTime) {
        395 var timeout = this.queue_.pop();
        396
        397 if (!(timeout.timeoutKey in this.deletedKeys_)) {
        398 // Only move time forwards.
        399 this.nowMillis_ = Math.max(this.nowMillis_,
        400 timeout.runAtMillis + this.timeoutDelay_);
        401 // Call timeout in global scope and pass the timeout key as the argument.
        402 timeout.funcToCall.call(goog.global, timeout.timeoutKey);
        403 // In case the interval was cleared in the funcToCall
        404 if (timeout.recurring) {
        405 this.scheduleFunction_(
        406 timeout.timeoutKey, timeout.funcToCall, timeout.millis, true);
        407 }
        408 }
        409 }
        410};
        411
        412
        413/**
        414 * Schedules a function to be run at a certain time.
        415 * @param {number} timeoutKey The timeout key.
        416 * @param {Function} funcToCall The function to call.
        417 * @param {number} millis The number of milliseconds to call it in.
        418 * @param {boolean} recurring Whether to function call should recur.
        419 * @private
        420 */
        421goog.testing.MockClock.prototype.scheduleFunction_ = function(
        422 timeoutKey, funcToCall, millis, recurring) {
        423 if (!goog.isFunction(funcToCall)) {
        424 // Early error for debuggability rather than dying in the next .tick()
        425 throw new TypeError('The provided callback must be a function, not a ' +
        426 typeof funcToCall);
        427 }
        428
        429 var timeout = {
        430 runAtMillis: this.nowMillis_ + millis,
        431 funcToCall: funcToCall,
        432 recurring: recurring,
        433 timeoutKey: timeoutKey,
        434 millis: millis
        435 };
        436
        437 goog.testing.MockClock.insert_(timeout, this.queue_);
        438};
        439
        440
        441/**
        442 * Inserts a timer descriptor into a descending-order queue.
        443 *
        444 * Later-inserted duplicates appear at lower indices. For example, the
        445 * asterisk in (5,4,*,3,2,1) would be the insertion point for 3.
        446 *
        447 * @param {Object} timeout The timeout to insert, with numerical runAtMillis
        448 * property.
        449 * @param {Array<Object>} queue The queue to insert into, with each element
        450 * having a numerical runAtMillis property.
        451 * @private
        452 */
        453goog.testing.MockClock.insert_ = function(timeout, queue) {
        454 // Although insertion of N items is quadratic, requiring goog.structs.Heap
        455 // from a unit test will make tests more prone to breakage. Since unit
        456 // tests are normally small, scalability is not a primary issue.
        457
        458 // Find an insertion point. Since the queue is in reverse order (so we
        459 // can pop rather than unshift), and later timers with the same time stamp
        460 // should be executed later, we look for the element strictly greater than
        461 // the one we are inserting.
        462
        463 for (var i = queue.length; i != 0; i--) {
        464 if (queue[i - 1].runAtMillis > timeout.runAtMillis) {
        465 break;
        466 }
        467 queue[i] = queue[i - 1];
        468 }
        469
        470 queue[i] = timeout;
        471};
        472
        473
        474/**
        475 * Maximum 32-bit signed integer.
        476 *
        477 * Timeouts over this time return immediately in many browsers, due to integer
        478 * overflow. Such known browsers include Firefox, Chrome, and Safari, but not
        479 * IE.
        480 *
        481 * @type {number}
        482 * @private
        483 */
        484goog.testing.MockClock.MAX_INT_ = 2147483647;
        485
        486
        487/**
        488 * Schedules a function to be called after {@code millis} milliseconds.
        489 * Mock implementation for setTimeout.
        490 * @param {Function} funcToCall The function to call.
        491 * @param {number=} opt_millis The number of milliseconds to call it after.
        492 * @return {number} The number of timeouts created.
        493 * @private
        494 */
        495goog.testing.MockClock.prototype.setTimeout_ = function(
        496 funcToCall, opt_millis) {
        497 var millis = opt_millis || 0;
        498 if (millis > goog.testing.MockClock.MAX_INT_) {
        499 throw Error(
        500 'Bad timeout value: ' + millis + '. Timeouts over MAX_INT ' +
        501 '(24.8 days) cause timeouts to be fired ' +
        502 'immediately in most browsers, except for IE.');
        503 }
        504 this.timeoutsMade_++;
        505 this.scheduleFunction_(goog.testing.MockClock.nextId, funcToCall, millis,
        506 false);
        507 return goog.testing.MockClock.nextId++;
        508};
        509
        510
        511/**
        512 * Schedules a function to be called every {@code millis} milliseconds.
        513 * Mock implementation for setInterval.
        514 * @param {Function} funcToCall The function to call.
        515 * @param {number=} opt_millis The number of milliseconds between calls.
        516 * @return {number} The number of timeouts created.
        517 * @private
        518 */
        519goog.testing.MockClock.prototype.setInterval_ =
        520 function(funcToCall, opt_millis) {
        521 var millis = opt_millis || 0;
        522 this.timeoutsMade_++;
        523 this.scheduleFunction_(goog.testing.MockClock.nextId, funcToCall, millis,
        524 true);
        525 return goog.testing.MockClock.nextId++;
        526};
        527
        528
        529/**
        530 * Schedules a function to be called when an animation frame is triggered.
        531 * Mock implementation for requestAnimationFrame.
        532 * @param {Function} funcToCall The function to call.
        533 * @return {number} The number of timeouts created.
        534 * @private
        535 */
        536goog.testing.MockClock.prototype.requestAnimationFrame_ = function(funcToCall) {
        537 return this.setTimeout_(goog.bind(function() {
        538 if (funcToCall) {
        539 funcToCall(this.getCurrentTime());
        540 } else if (goog.global.mozRequestAnimationFrame) {
        541 var event = new goog.testing.events.Event('MozBeforePaint', goog.global);
        542 event['timeStamp'] = this.getCurrentTime();
        543 goog.testing.events.fireBrowserEvent(event);
        544 }
        545 }, this), goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT);
        546};
        547
        548
        549/**
        550 * Schedules a function to be called immediately after the current JS
        551 * execution.
        552 * Mock implementation for setImmediate.
        553 * @param {Function} funcToCall The function to call.
        554 * @return {number} The number of timeouts created.
        555 * @private
        556 */
        557goog.testing.MockClock.prototype.setImmediate_ = function(funcToCall) {
        558 return this.setTimeout_(funcToCall, 0);
        559};
        560
        561
        562/**
        563 * Clears a timeout.
        564 * Mock implementation for clearTimeout.
        565 * @param {number} timeoutKey The timeout key to clear.
        566 * @private
        567 */
        568goog.testing.MockClock.prototype.clearTimeout_ = function(timeoutKey) {
        569 // Some common libraries register static state with timers.
        570 // This is bad. It leads to all sorts of crazy test problems where
        571 // 1) Test A sets up a new mock clock and a static timer.
        572 // 2) Test B sets up a new mock clock, but re-uses the static timer
        573 // from Test A.
        574 // 3) A timeout key from test A gets cleared, breaking a timeout in
        575 // Test B.
        576 //
        577 // For now, we just hackily fail silently if someone tries to clear a timeout
        578 // key before we've allocated it.
        579 // Ideally, we should throw an exception if we see this happening.
        580 if (this.isTimeoutSet(timeoutKey)) {
        581 this.deletedKeys_[timeoutKey] = true;
        582 }
        583};
        584
        585
        586/**
        587 * Clears an interval.
        588 * Mock implementation for clearInterval.
        589 * @param {number} timeoutKey The interval key to clear.
        590 * @private
        591 */
        592goog.testing.MockClock.prototype.clearInterval_ = function(timeoutKey) {
        593 this.clearTimeout_(timeoutKey);
        594};
        595
        596
        597/**
        598 * Clears a requestAnimationFrame.
        599 * Mock implementation for cancelRequestAnimationFrame.
        600 * @param {number} timeoutKey The requestAnimationFrame key to clear.
        601 * @private
        602 */
        603goog.testing.MockClock.prototype.cancelRequestAnimationFrame_ =
        604 function(timeoutKey) {
        605 this.clearTimeout_(timeoutKey);
        606};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/mockcontrol.js.src.html b/docs/source/lib/goog/testing/mockcontrol.js.src.html new file mode 100644 index 0000000..f596062 --- /dev/null +++ b/docs/source/lib/goog/testing/mockcontrol.js.src.html @@ -0,0 +1 @@ +mockcontrol.js

        lib/goog/testing/mockcontrol.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 A MockControl holds a set of mocks for a particular test.
        17 * It consolidates calls to $replay, $verify, and $tearDown, which simplifies
        18 * the test and helps avoid omissions.
        19 *
        20 * You can create and control a mock:
        21 * var mockFoo = mockControl.addMock(new MyMock(Foo));
        22 *
        23 * MockControl also exposes some convenience functions that create
        24 * controlled mocks for common mocks: StrictMock, LooseMock,
        25 * FunctionMock, MethodMock, and GlobalFunctionMock.
        26 *
        27 */
        28
        29
        30goog.provide('goog.testing.MockControl');
        31
        32goog.require('goog.array');
        33goog.require('goog.testing');
        34goog.require('goog.testing.LooseMock');
        35goog.require('goog.testing.StrictMock');
        36
        37
        38
        39/**
        40 * Controls a set of mocks. Controlled mocks are replayed, verified, and
        41 * cleaned-up at the same time.
        42 * @constructor
        43 */
        44goog.testing.MockControl = function() {
        45 /**
        46 * The list of mocks being controlled.
        47 * @type {Array<goog.testing.MockInterface>}
        48 * @private
        49 */
        50 this.mocks_ = [];
        51};
        52
        53
        54/**
        55 * Takes control of this mock.
        56 * @param {goog.testing.MockInterface} mock Mock to be controlled.
        57 * @return {goog.testing.MockInterface} The same mock passed in,
        58 * for convenience.
        59 */
        60goog.testing.MockControl.prototype.addMock = function(mock) {
        61 this.mocks_.push(mock);
        62 return mock;
        63};
        64
        65
        66/**
        67 * Calls replay on each controlled mock.
        68 */
        69goog.testing.MockControl.prototype.$replayAll = function() {
        70 goog.array.forEach(this.mocks_, function(m) {
        71 m.$replay();
        72 });
        73};
        74
        75
        76/**
        77 * Calls reset on each controlled mock.
        78 */
        79goog.testing.MockControl.prototype.$resetAll = function() {
        80 goog.array.forEach(this.mocks_, function(m) {
        81 m.$reset();
        82 });
        83};
        84
        85
        86/**
        87 * Calls verify on each controlled mock.
        88 */
        89goog.testing.MockControl.prototype.$verifyAll = function() {
        90 goog.array.forEach(this.mocks_, function(m) {
        91 m.$verify();
        92 });
        93};
        94
        95
        96/**
        97 * Calls tearDown on each controlled mock, if necesssary.
        98 */
        99goog.testing.MockControl.prototype.$tearDown = function() {
        100 goog.array.forEach(this.mocks_, function(m) {
        101 // $tearDown if defined.
        102 if (m.$tearDown) {
        103 m.$tearDown();
        104 }
        105 // TODO(user): Somehow determine if verifyAll should have been called
        106 // but was not.
        107 });
        108};
        109
        110
        111/**
        112 * Creates a controlled StrictMock. Passes its arguments through to the
        113 * StrictMock constructor.
        114 * @param {Object|Function} objectToMock The object that should be mocked, or
        115 * the constructor of an object to mock.
        116 * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
        117 * a mock should be constructed from the static functions of a class.
        118 * @param {boolean=} opt_createProxy An optional argument denoting that
        119 * a proxy for the target mock should be created.
        120 * @return {!goog.testing.StrictMock} The mock object.
        121 */
        122goog.testing.MockControl.prototype.createStrictMock = function(
        123 objectToMock, opt_mockStaticMethods, opt_createProxy) {
        124 var m = new goog.testing.StrictMock(objectToMock, opt_mockStaticMethods,
        125 opt_createProxy);
        126 this.addMock(m);
        127 return m;
        128};
        129
        130
        131/**
        132 * Creates a controlled LooseMock. Passes its arguments through to the
        133 * LooseMock constructor.
        134 * @param {Object|Function} objectToMock The object that should be mocked, or
        135 * the constructor of an object to mock.
        136 * @param {boolean=} opt_ignoreUnexpectedCalls Whether to ignore unexpected
        137 * calls.
        138 * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
        139 * a mock should be constructed from the static functions of a class.
        140 * @param {boolean=} opt_createProxy An optional argument denoting that
        141 * a proxy for the target mock should be created.
        142 * @return {!goog.testing.LooseMock} The mock object.
        143 */
        144goog.testing.MockControl.prototype.createLooseMock = function(
        145 objectToMock, opt_ignoreUnexpectedCalls,
        146 opt_mockStaticMethods, opt_createProxy) {
        147 var m = new goog.testing.LooseMock(objectToMock, opt_ignoreUnexpectedCalls,
        148 opt_mockStaticMethods, opt_createProxy);
        149 this.addMock(m);
        150 return m;
        151};
        152
        153
        154/**
        155 * Creates a controlled FunctionMock. Passes its arguments through to the
        156 * FunctionMock constructor.
        157 * @param {string=} opt_functionName The optional name of the function to mock
        158 * set to '[anonymous mocked function]' if not passed in.
        159 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        160 * goog.testing.Mock.STRICT. The default is STRICT.
        161 * @return {goog.testing.MockInterface} The mocked function.
        162 */
        163goog.testing.MockControl.prototype.createFunctionMock = function(
        164 opt_functionName, opt_strictness) {
        165 var m = goog.testing.createFunctionMock(opt_functionName, opt_strictness);
        166 this.addMock(m);
        167 return m;
        168};
        169
        170
        171/**
        172 * Creates a controlled MethodMock. Passes its arguments through to the
        173 * MethodMock constructor.
        174 * @param {Object} scope The scope of the method to be mocked out.
        175 * @param {string} functionName The name of the function we're going to mock.
        176 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        177 * goog.testing.Mock.STRICT. The default is STRICT.
        178 * @return {!goog.testing.MockInterface} The mocked method.
        179 */
        180goog.testing.MockControl.prototype.createMethodMock = function(
        181 scope, functionName, opt_strictness) {
        182 var m = goog.testing.createMethodMock(scope, functionName, opt_strictness);
        183 this.addMock(m);
        184 return m;
        185};
        186
        187
        188/**
        189 * Creates a controlled MethodMock for a constructor. Passes its arguments
        190 * through to the MethodMock constructor. See
        191 * {@link goog.testing.createConstructorMock} for details.
        192 * @param {Object} scope The scope of the constructor to be mocked out.
        193 * @param {string} constructorName The name of the function we're going to mock.
        194 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        195 * goog.testing.Mock.STRICT. The default is STRICT.
        196 * @return {!goog.testing.MockInterface} The mocked method.
        197 */
        198goog.testing.MockControl.prototype.createConstructorMock = function(
        199 scope, constructorName, opt_strictness) {
        200 var m = goog.testing.createConstructorMock(scope, constructorName,
        201 opt_strictness);
        202 this.addMock(m);
        203 return m;
        204};
        205
        206
        207/**
        208 * Creates a controlled GlobalFunctionMock. Passes its arguments through to the
        209 * GlobalFunctionMock constructor.
        210 * @param {string} functionName The name of the function we're going to mock.
        211 * @param {number=} opt_strictness One of goog.testing.Mock.LOOSE or
        212 * goog.testing.Mock.STRICT. The default is STRICT.
        213 * @return {!goog.testing.MockInterface} The mocked function.
        214 */
        215goog.testing.MockControl.prototype.createGlobalFunctionMock = function(
        216 functionName, opt_strictness) {
        217 var m = goog.testing.createGlobalFunctionMock(functionName, opt_strictness);
        218 this.addMock(m);
        219 return m;
        220};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/mockinterface.js.src.html b/docs/source/lib/goog/testing/mockinterface.js.src.html new file mode 100644 index 0000000..b644f39 --- /dev/null +++ b/docs/source/lib/goog/testing/mockinterface.js.src.html @@ -0,0 +1 @@ +mockinterface.js

        lib/goog/testing/mockinterface.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 An interface that all mocks should share.
        17 * @author nicksantos@google.com (Nick Santos)
        18 */
        19
        20goog.provide('goog.testing.MockInterface');
        21
        22
        23
        24/** @interface */
        25goog.testing.MockInterface = function() {};
        26
        27
        28/**
        29 * Write down all the expected functions that have been called on the
        30 * mock so far. From here on out, future function calls will be
        31 * compared against this list.
        32 */
        33goog.testing.MockInterface.prototype.$replay = function() {};
        34
        35
        36/**
        37 * Reset the mock.
        38 */
        39goog.testing.MockInterface.prototype.$reset = function() {};
        40
        41
        42/**
        43 * Assert that the expected function calls match the actual calls.
        44 */
        45goog.testing.MockInterface.prototype.$verify = function() {};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/mockmatchers.js.src.html b/docs/source/lib/goog/testing/mockmatchers.js.src.html new file mode 100644 index 0000000..fda5916 --- /dev/null +++ b/docs/source/lib/goog/testing/mockmatchers.js.src.html @@ -0,0 +1 @@ +mockmatchers.js

        lib/goog/testing/mockmatchers.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 Matchers to be used with the mock utilities. They allow for
        17 * flexible matching by type. Custom matchers can be created by passing a
        18 * matcher function into an ArgumentMatcher instance.
        19 *
        20 * For examples, please see the unit test.
        21 *
        22 */
        23
        24
        25goog.provide('goog.testing.mockmatchers');
        26goog.provide('goog.testing.mockmatchers.ArgumentMatcher');
        27goog.provide('goog.testing.mockmatchers.IgnoreArgument');
        28goog.provide('goog.testing.mockmatchers.InstanceOf');
        29goog.provide('goog.testing.mockmatchers.ObjectEquals');
        30goog.provide('goog.testing.mockmatchers.RegexpMatch');
        31goog.provide('goog.testing.mockmatchers.SaveArgument');
        32goog.provide('goog.testing.mockmatchers.TypeOf');
        33
        34goog.require('goog.array');
        35goog.require('goog.dom');
        36goog.require('goog.testing.asserts');
        37
        38
        39
        40/**
        41 * A simple interface for executing argument matching. A match in this case is
        42 * testing to see if a supplied object fits a given criteria. True is returned
        43 * if the given criteria is met.
        44 * @param {Function=} opt_matchFn A function that evaluates a given argument
        45 * and returns true if it meets a given criteria.
        46 * @param {?string=} opt_matchName The name expressing intent as part of
        47 * an error message for when a match fails.
        48 * @constructor
        49 */
        50goog.testing.mockmatchers.ArgumentMatcher =
        51 function(opt_matchFn, opt_matchName) {
        52 /**
        53 * A function that evaluates a given argument and returns true if it meets a
        54 * given criteria.
        55 * @type {Function}
        56 * @private
        57 */
        58 this.matchFn_ = opt_matchFn || null;
        59
        60 /**
        61 * A string indicating the match intent (e.g. isBoolean or isString).
        62 * @type {?string}
        63 * @private
        64 */
        65 this.matchName_ = opt_matchName || null;
        66};
        67
        68
        69/**
        70 * A function that takes a match argument and an optional MockExpectation
        71 * which (if provided) will get error information and returns whether or
        72 * not it matches.
        73 * @param {*} toVerify The argument that should be verified.
        74 * @param {goog.testing.MockExpectation?=} opt_expectation The expectation
        75 * for this match.
        76 * @return {boolean} Whether or not a given argument passes verification.
        77 */
        78goog.testing.mockmatchers.ArgumentMatcher.prototype.matches =
        79 function(toVerify, opt_expectation) {
        80 if (this.matchFn_) {
        81 var isamatch = this.matchFn_(toVerify);
        82 if (!isamatch && opt_expectation) {
        83 if (this.matchName_) {
        84 opt_expectation.addErrorMessage('Expected: ' +
        85 this.matchName_ + ' but was: ' + _displayStringForValue(toVerify));
        86 } else {
        87 opt_expectation.addErrorMessage('Expected: missing mockmatcher' +
        88 ' description but was: ' +
        89 _displayStringForValue(toVerify));
        90 }
        91 }
        92 return isamatch;
        93 } else {
        94 throw Error('No match function defined for this mock matcher');
        95 }
        96};
        97
        98
        99
        100/**
        101 * A matcher that verifies that an argument is an instance of a given class.
        102 * @param {Function} ctor The class that will be used for verification.
        103 * @constructor
        104 * @extends {goog.testing.mockmatchers.ArgumentMatcher}
        105 * @final
        106 */
        107goog.testing.mockmatchers.InstanceOf = function(ctor) {
        108 goog.testing.mockmatchers.ArgumentMatcher.call(this,
        109 function(obj) {
        110 return obj instanceof ctor;
        111 // NOTE: Browser differences on ctor.toString() output
        112 // make using that here problematic. So for now, just let
        113 // people know the instanceOf() failed without providing
        114 // browser specific details...
        115 }, 'instanceOf()');
        116};
        117goog.inherits(goog.testing.mockmatchers.InstanceOf,
        118 goog.testing.mockmatchers.ArgumentMatcher);
        119
        120
        121
        122/**
        123 * A matcher that verifies that an argument is of a given type (e.g. "object").
        124 * @param {string} type The type that a given argument must have.
        125 * @constructor
        126 * @extends {goog.testing.mockmatchers.ArgumentMatcher}
        127 * @final
        128 */
        129goog.testing.mockmatchers.TypeOf = function(type) {
        130 goog.testing.mockmatchers.ArgumentMatcher.call(this,
        131 function(obj) {
        132 return goog.typeOf(obj) == type;
        133 }, 'typeOf(' + type + ')');
        134};
        135goog.inherits(goog.testing.mockmatchers.TypeOf,
        136 goog.testing.mockmatchers.ArgumentMatcher);
        137
        138
        139
        140/**
        141 * A matcher that verifies that an argument matches a given RegExp.
        142 * @param {RegExp} regexp The regular expression that the argument must match.
        143 * @constructor
        144 * @extends {goog.testing.mockmatchers.ArgumentMatcher}
        145 * @final
        146 */
        147goog.testing.mockmatchers.RegexpMatch = function(regexp) {
        148 goog.testing.mockmatchers.ArgumentMatcher.call(this,
        149 function(str) {
        150 return regexp.test(str);
        151 }, 'match(' + regexp + ')');
        152};
        153goog.inherits(goog.testing.mockmatchers.RegexpMatch,
        154 goog.testing.mockmatchers.ArgumentMatcher);
        155
        156
        157
        158/**
        159 * A matcher that always returns true. It is useful when the user does not care
        160 * for some arguments.
        161 * For example: mockFunction('username', 'password', IgnoreArgument);
        162 * @constructor
        163 * @extends {goog.testing.mockmatchers.ArgumentMatcher}
        164 * @final
        165 */
        166goog.testing.mockmatchers.IgnoreArgument = function() {
        167 goog.testing.mockmatchers.ArgumentMatcher.call(this,
        168 function() {
        169 return true;
        170 }, 'true');
        171};
        172goog.inherits(goog.testing.mockmatchers.IgnoreArgument,
        173 goog.testing.mockmatchers.ArgumentMatcher);
        174
        175
        176
        177/**
        178 * A matcher that verifies that the argument is an object that equals the given
        179 * expected object, using a deep comparison.
        180 * @param {Object} expectedObject An object to match against when
        181 * verifying the argument.
        182 * @constructor
        183 * @extends {goog.testing.mockmatchers.ArgumentMatcher}
        184 */
        185goog.testing.mockmatchers.ObjectEquals = function(expectedObject) {
        186 goog.testing.mockmatchers.ArgumentMatcher.call(this,
        187 function(matchObject) {
        188 assertObjectEquals('Expected equal objects', expectedObject,
        189 matchObject);
        190 return true;
        191 }, 'objectEquals(' + expectedObject + ')');
        192};
        193goog.inherits(goog.testing.mockmatchers.ObjectEquals,
        194 goog.testing.mockmatchers.ArgumentMatcher);
        195
        196
        197/** @override */
        198goog.testing.mockmatchers.ObjectEquals.prototype.matches =
        199 function(toVerify, opt_expectation) {
        200 // Override the default matches implementation to capture the exception thrown
        201 // by assertObjectEquals (if any) and add that message to the expectation.
        202 try {
        203 return goog.testing.mockmatchers.ObjectEquals.superClass_.matches.call(
        204 this, toVerify, opt_expectation);
        205 } catch (e) {
        206 if (opt_expectation) {
        207 opt_expectation.addErrorMessage(e.message);
        208 }
        209 return false;
        210 }
        211};
        212
        213
        214
        215/**
        216 * A matcher that saves the argument that it is verifying so that your unit test
        217 * can perform extra tests with this argument later. For example, if the
        218 * argument is a callback method, the unit test can then later call this
        219 * callback to test the asynchronous portion of the call.
        220 * @param {goog.testing.mockmatchers.ArgumentMatcher|Function=} opt_matcher
        221 * Argument matcher or matching function that will be used to validate the
        222 * argument. By default, argument will always be valid.
        223 * @param {?string=} opt_matchName The name expressing intent as part of
        224 * an error message for when a match fails.
        225 * @constructor
        226 * @extends {goog.testing.mockmatchers.ArgumentMatcher}
        227 * @final
        228 */
        229goog.testing.mockmatchers.SaveArgument = function(opt_matcher, opt_matchName) {
        230 goog.testing.mockmatchers.ArgumentMatcher.call(
        231 this, /** @type {Function} */ (opt_matcher), opt_matchName);
        232
        233 if (opt_matcher instanceof goog.testing.mockmatchers.ArgumentMatcher) {
        234 /**
        235 * Delegate match requests to this matcher.
        236 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        237 * @private
        238 */
        239 this.delegateMatcher_ = opt_matcher;
        240 } else if (!opt_matcher) {
        241 this.delegateMatcher_ = goog.testing.mockmatchers.ignoreArgument;
        242 }
        243};
        244goog.inherits(goog.testing.mockmatchers.SaveArgument,
        245 goog.testing.mockmatchers.ArgumentMatcher);
        246
        247
        248/** @override */
        249goog.testing.mockmatchers.SaveArgument.prototype.matches = function(
        250 toVerify, opt_expectation) {
        251 this.arg = toVerify;
        252 if (this.delegateMatcher_) {
        253 return this.delegateMatcher_.matches(toVerify, opt_expectation);
        254 }
        255 return goog.testing.mockmatchers.SaveArgument.superClass_.matches.call(
        256 this, toVerify, opt_expectation);
        257};
        258
        259
        260/**
        261 * Saved argument that was verified.
        262 * @type {*}
        263 */
        264goog.testing.mockmatchers.SaveArgument.prototype.arg;
        265
        266
        267/**
        268 * An instance of the IgnoreArgument matcher. Returns true for all matches.
        269 * @type {goog.testing.mockmatchers.IgnoreArgument}
        270 */
        271goog.testing.mockmatchers.ignoreArgument =
        272 new goog.testing.mockmatchers.IgnoreArgument();
        273
        274
        275/**
        276 * A matcher that verifies that an argument is an array.
        277 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        278 */
        279goog.testing.mockmatchers.isArray =
        280 new goog.testing.mockmatchers.ArgumentMatcher(goog.isArray,
        281 'isArray');
        282
        283
        284/**
        285 * A matcher that verifies that an argument is a array-like. A NodeList is an
        286 * example of a collection that is very close to an array.
        287 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        288 */
        289goog.testing.mockmatchers.isArrayLike =
        290 new goog.testing.mockmatchers.ArgumentMatcher(goog.isArrayLike,
        291 'isArrayLike');
        292
        293
        294/**
        295 * A matcher that verifies that an argument is a date-like.
        296 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        297 */
        298goog.testing.mockmatchers.isDateLike =
        299 new goog.testing.mockmatchers.ArgumentMatcher(goog.isDateLike,
        300 'isDateLike');
        301
        302
        303/**
        304 * A matcher that verifies that an argument is a string.
        305 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        306 */
        307goog.testing.mockmatchers.isString =
        308 new goog.testing.mockmatchers.ArgumentMatcher(goog.isString,
        309 'isString');
        310
        311
        312/**
        313 * A matcher that verifies that an argument is a boolean.
        314 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        315 */
        316goog.testing.mockmatchers.isBoolean =
        317 new goog.testing.mockmatchers.ArgumentMatcher(goog.isBoolean,
        318 'isBoolean');
        319
        320
        321/**
        322 * A matcher that verifies that an argument is a number.
        323 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        324 */
        325goog.testing.mockmatchers.isNumber =
        326 new goog.testing.mockmatchers.ArgumentMatcher(goog.isNumber,
        327 'isNumber');
        328
        329
        330/**
        331 * A matcher that verifies that an argument is a function.
        332 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        333 */
        334goog.testing.mockmatchers.isFunction =
        335 new goog.testing.mockmatchers.ArgumentMatcher(goog.isFunction,
        336 'isFunction');
        337
        338
        339/**
        340 * A matcher that verifies that an argument is an object.
        341 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        342 */
        343goog.testing.mockmatchers.isObject =
        344 new goog.testing.mockmatchers.ArgumentMatcher(goog.isObject,
        345 'isObject');
        346
        347
        348/**
        349 * A matcher that verifies that an argument is like a DOM node.
        350 * @type {goog.testing.mockmatchers.ArgumentMatcher}
        351 */
        352goog.testing.mockmatchers.isNodeLike =
        353 new goog.testing.mockmatchers.ArgumentMatcher(goog.dom.isNodeLike,
        354 'isNodeLike');
        355
        356
        357/**
        358 * A function that checks to see if an array matches a given set of
        359 * expectations. The expectations array can be a mix of ArgumentMatcher
        360 * implementations and values. True will be returned if values are identical or
        361 * if a matcher returns a positive result.
        362 * @param {Array<?>} expectedArr An array of expectations which can be either
        363 * values to check for equality or ArgumentMatchers.
        364 * @param {Array<?>} arr The array to match.
        365 * @param {goog.testing.MockExpectation?=} opt_expectation The expectation
        366 * for this match.
        367 * @return {boolean} Whether or not the given array matches the expectations.
        368 */
        369goog.testing.mockmatchers.flexibleArrayMatcher =
        370 function(expectedArr, arr, opt_expectation) {
        371 return goog.array.equals(expectedArr, arr, function(a, b) {
        372 var errCount = 0;
        373 if (opt_expectation) {
        374 errCount = opt_expectation.getErrorMessageCount();
        375 }
        376 var isamatch = a === b ||
        377 a instanceof goog.testing.mockmatchers.ArgumentMatcher &&
        378 a.matches(b, opt_expectation);
        379 var failureMessage = null;
        380 if (!isamatch) {
        381 failureMessage = goog.testing.asserts.findDifferences(a, b);
        382 isamatch = !failureMessage;
        383 }
        384 if (!isamatch && opt_expectation) {
        385 // If the error count changed, the match sent out an error
        386 // message. If the error count has not changed, then
        387 // we need to send out an error message...
        388 if (errCount == opt_expectation.getErrorMessageCount()) {
        389 // Use the _displayStringForValue() from assert.js
        390 // for consistency...
        391 if (!failureMessage) {
        392 failureMessage = 'Expected: ' + _displayStringForValue(a) +
        393 ' but was: ' + _displayStringForValue(b);
        394 }
        395 opt_expectation.addErrorMessage(failureMessage);
        396 }
        397 }
        398 return isamatch;
        399 });
        400};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/objectpropertystring.js.src.html b/docs/source/lib/goog/testing/objectpropertystring.js.src.html new file mode 100644 index 0000000..df83670 --- /dev/null +++ b/docs/source/lib/goog/testing/objectpropertystring.js.src.html @@ -0,0 +1 @@ +objectpropertystring.js

        lib/goog/testing/objectpropertystring.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 Helper for passing property names as string literals in
        17 * compiled test code.
        18 *
        19 */
        20
        21goog.provide('goog.testing.ObjectPropertyString');
        22
        23
        24
        25/**
        26 * Object to pass a property name as a string literal and its containing object
        27 * when the JSCompiler is rewriting these names. This should only be used in
        28 * test code.
        29 *
        30 * @param {Object} object The containing object.
        31 * @param {Object|string} propertyString Property name as a string literal.
        32 * @constructor
        33 * @final
        34 */
        35goog.testing.ObjectPropertyString = function(object, propertyString) {
        36 this.object_ = object;
        37 this.propertyString_ = /** @type {string} */ (propertyString);
        38};
        39
        40
        41/**
        42 * @type {Object}
        43 * @private
        44 */
        45goog.testing.ObjectPropertyString.prototype.object_;
        46
        47
        48/**
        49 * @type {string}
        50 * @private
        51 */
        52goog.testing.ObjectPropertyString.prototype.propertyString_;
        53
        54
        55/**
        56 * @return {Object} The object.
        57 */
        58goog.testing.ObjectPropertyString.prototype.getObject = function() {
        59 return this.object_;
        60};
        61
        62
        63/**
        64 * @return {string} The property string.
        65 */
        66goog.testing.ObjectPropertyString.prototype.getPropertyString = function() {
        67 return this.propertyString_;
        68};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/propertyreplacer.js.src.html b/docs/source/lib/goog/testing/propertyreplacer.js.src.html new file mode 100644 index 0000000..fd59747 --- /dev/null +++ b/docs/source/lib/goog/testing/propertyreplacer.js.src.html @@ -0,0 +1 @@ +propertyreplacer.js

        lib/goog/testing/propertyreplacer.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 Helper class for creating stubs for testing.
        17 *
        18 */
        19
        20goog.provide('goog.testing.PropertyReplacer');
        21
        22/** @suppress {extraRequire} Needed for some tests to compile. */
        23goog.require('goog.testing.ObjectPropertyString');
        24goog.require('goog.userAgent');
        25
        26
        27
        28/**
        29 * Helper class for stubbing out variables and object properties for unit tests.
        30 * This class can change the value of some variables before running the test
        31 * cases, and to reset them in the tearDown phase.
        32 * See googletest.StubOutForTesting as an analogy in Python:
        33 * http://protobuf.googlecode.com/svn/trunk/python/stubout.py
        34 *
        35 * Example usage:
        36 *
        37 * var stubs = new goog.testing.PropertyReplacer();
        38 *
        39 * function setUp() {
        40 * // Mock functions used in all test cases.
        41 * stubs.set(Math, 'random', function() {
        42 * return 4; // Chosen by fair dice roll. Guaranteed to be random.
        43 * });
        44 * }
        45 *
        46 * function tearDown() {
        47 * stubs.reset();
        48 * }
        49 *
        50 * function testThreeDice() {
        51 * // Mock a constant used only in this test case.
        52 * stubs.set(goog.global, 'DICE_COUNT', 3);
        53 * assertEquals(12, rollAllDice());
        54 * }
        55 *
        56 * Constraints on altered objects:
        57 * <ul>
        58 * <li>DOM subclasses aren't supported.
        59 * <li>The value of the objects' constructor property must either be equal to
        60 * the real constructor or kept untouched.
        61 * </ul>
        62 *
        63 * @constructor
        64 * @final
        65 */
        66goog.testing.PropertyReplacer = function() {
        67 /**
        68 * Stores the values changed by the set() method in chronological order.
        69 * Its items are objects with 3 fields: 'object', 'key', 'value'. The
        70 * original value for the given key in the given object is stored under the
        71 * 'value' key.
        72 * @type {Array<{ object: ?, key: string, value: ? }>}
        73 * @private
        74 */
        75 this.original_ = [];
        76};
        77
        78
        79/**
        80 * Indicates that a key didn't exist before having been set by the set() method.
        81 * @private @const
        82 */
        83goog.testing.PropertyReplacer.NO_SUCH_KEY_ = {};
        84
        85
        86/**
        87 * Tells if the given key exists in the object. Ignores inherited fields.
        88 * @param {Object|Function} obj The JavaScript or native object or function
        89 * whose key is to be checked.
        90 * @param {string} key The key to check.
        91 * @return {boolean} Whether the object has the key as own key.
        92 * @private
        93 */
        94goog.testing.PropertyReplacer.hasKey_ = function(obj, key) {
        95 if (!(key in obj)) {
        96 return false;
        97 }
        98 // hasOwnProperty is only reliable with JavaScript objects. It returns false
        99 // for built-in DOM attributes.
        100 if (Object.prototype.hasOwnProperty.call(obj, key)) {
        101 return true;
        102 }
        103 // In all browsers except Opera obj.constructor never equals to Object if
        104 // obj is an instance of a native class. In Opera we have to fall back on
        105 // examining obj.toString().
        106 if (obj.constructor == Object &&
        107 (!goog.userAgent.OPERA ||
        108 Object.prototype.toString.call(obj) == '[object Object]')) {
        109 return false;
        110 }
        111 try {
        112 // Firefox hack to consider "className" part of the HTML elements or
        113 // "body" part of document. Although they are defined in the prototype of
        114 // HTMLElement or Document, accessing them this way throws an exception.
        115 // <pre>
        116 // var dummy = document.body.constructor.prototype.className
        117 // [Exception... "Cannot modify properties of a WrappedNative"]
        118 // </pre>
        119 var dummy = obj.constructor.prototype[key];
        120 } catch (e) {
        121 return true;
        122 }
        123 return !(key in obj.constructor.prototype);
        124};
        125
        126
        127/**
        128 * Deletes a key from an object. Sets it to undefined or empty string if the
        129 * delete failed.
        130 * @param {Object|Function} obj The object or function to delete a key from.
        131 * @param {string} key The key to delete.
        132 * @throws {Error} In case of trying to set a read-only property
        133 * @private
        134 */
        135goog.testing.PropertyReplacer.deleteKey_ = function(obj, key) {
        136 try {
        137 delete obj[key];
        138 // Delete has no effect for built-in properties of DOM nodes in FF.
        139 if (!goog.testing.PropertyReplacer.hasKey_(obj, key)) {
        140 return;
        141 }
        142 } catch (e) {
        143 // IE throws TypeError when trying to delete properties of native objects
        144 // (e.g. DOM nodes or window), even if they have been added by JavaScript.
        145 }
        146
        147 obj[key] = undefined;
        148 if (obj[key] == 'undefined') {
        149 // Some properties such as className in IE are always evaluated as string
        150 // so undefined will become 'undefined'.
        151 obj[key] = '';
        152 }
        153
        154 if (obj[key]) {
        155 throw Error('Cannot delete non configurable property "' + key + '" in ' +
        156 obj);
        157 }
        158};
        159
        160
        161/**
        162 * Adds or changes a value in an object while saving its original state.
        163 * @param {Object|Function} obj The JavaScript or native object or function to
        164 * alter. See the constraints in the class description.
        165 * @param {string} key The key to change the value for.
        166 * @param {*} value The new value to set.
        167 * @throws {Error} In case of trying to set a read-only property.
        168 */
        169goog.testing.PropertyReplacer.prototype.set = function(obj, key, value) {
        170 var origValue = goog.testing.PropertyReplacer.hasKey_(obj, key) ? obj[key] :
        171 goog.testing.PropertyReplacer.NO_SUCH_KEY_;
        172 this.original_.push({object: obj, key: key, value: origValue});
        173 obj[key] = value;
        174
        175 // Check whether obj[key] was a read-only value and the assignment failed.
        176 // Also, check that we're not comparing returned pixel values when "value"
        177 // is 0. In other words, account for this case:
        178 // document.body.style.margin = 0;
        179 // document.body.style.margin; // returns "0px"
        180 if (obj[key] != value && (value + 'px') != obj[key]) {
        181 throw Error('Cannot overwrite read-only property "' + key + '" in ' + obj);
        182 }
        183};
        184
        185
        186/**
        187 * Changes an existing value in an object to another one of the same type while
        188 * saving its original state. The advantage of {@code replace} over {@link #set}
        189 * is that {@code replace} protects against typos and erroneously passing tests
        190 * after some members have been renamed during a refactoring.
        191 * @param {Object|Function} obj The JavaScript or native object or function to
        192 * alter. See the constraints in the class description.
        193 * @param {string} key The key to change the value for. It has to be present
        194 * either in {@code obj} or in its prototype chain.
        195 * @param {*} value The new value to set. It has to have the same type as the
        196 * original value. The types are compared with {@link goog.typeOf}.
        197 * @throws {Error} In case of missing key or type mismatch.
        198 */
        199goog.testing.PropertyReplacer.prototype.replace = function(obj, key, value) {
        200 if (!(key in obj)) {
        201 throw Error('Cannot replace missing property "' + key + '" in ' + obj);
        202 }
        203 if (goog.typeOf(obj[key]) != goog.typeOf(value)) {
        204 throw Error('Cannot replace property "' + key + '" in ' + obj +
        205 ' with a value of different type');
        206 }
        207 this.set(obj, key, value);
        208};
        209
        210
        211/**
        212 * Builds an object structure for the provided namespace path. Doesn't
        213 * overwrite those prefixes of the path that are already objects or functions.
        214 * @param {string} path The path to create or alter, e.g. 'goog.ui.Menu'.
        215 * @param {*} value The value to set.
        216 */
        217goog.testing.PropertyReplacer.prototype.setPath = function(path, value) {
        218 var parts = path.split('.');
        219 var obj = goog.global;
        220 for (var i = 0; i < parts.length - 1; i++) {
        221 var part = parts[i];
        222 if (part == 'prototype' && !obj[part]) {
        223 throw Error('Cannot set the prototype of ' + parts.slice(0, i).join('.'));
        224 }
        225 if (!goog.isObject(obj[part]) && !goog.isFunction(obj[part])) {
        226 this.set(obj, part, {});
        227 }
        228 obj = obj[part];
        229 }
        230 this.set(obj, parts[parts.length - 1], value);
        231};
        232
        233
        234/**
        235 * Deletes the key from the object while saving its original value.
        236 * @param {Object|Function} obj The JavaScript or native object or function to
        237 * alter. See the constraints in the class description.
        238 * @param {string} key The key to delete.
        239 */
        240goog.testing.PropertyReplacer.prototype.remove = function(obj, key) {
        241 if (goog.testing.PropertyReplacer.hasKey_(obj, key)) {
        242 this.original_.push({object: obj, key: key, value: obj[key]});
        243 goog.testing.PropertyReplacer.deleteKey_(obj, key);
        244 }
        245};
        246
        247
        248/**
        249 * Resets all changes made by goog.testing.PropertyReplacer.prototype.set.
        250 */
        251goog.testing.PropertyReplacer.prototype.reset = function() {
        252 for (var i = this.original_.length - 1; i >= 0; i--) {
        253 var original = this.original_[i];
        254 if (original.value == goog.testing.PropertyReplacer.NO_SUCH_KEY_) {
        255 goog.testing.PropertyReplacer.deleteKey_(original.object, original.key);
        256 } else {
        257 original.object[original.key] = original.value;
        258 }
        259 delete this.original_[i];
        260 }
        261 this.original_.length = 0;
        262};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/recordfunction.js.src.html b/docs/source/lib/goog/testing/recordfunction.js.src.html new file mode 100644 index 0000000..2a596df --- /dev/null +++ b/docs/source/lib/goog/testing/recordfunction.js.src.html @@ -0,0 +1 @@ +recordfunction.js

        lib/goog/testing/recordfunction.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 Helper class for recording the calls of a function.
        17 *
        18 * Example:
        19 * <pre>
        20 * var stubs = new goog.testing.PropertyReplacer();
        21 *
        22 * function tearDown() {
        23 * stubs.reset();
        24 * }
        25 *
        26 * function testShuffle() {
        27 * stubs.set(Math, 'random', goog.testing.recordFunction(Math.random));
        28 * var arr = shuffle([1, 2, 3, 4, 5]);
        29 * assertSameElements([1, 2, 3, 4, 5], arr);
        30 * assertEquals(4, Math.random.getCallCount());
        31 * }
        32 *
        33 * function testOpenDialog() {
        34 * stubs.set(goog.ui, 'Dialog',
        35 * goog.testing.recordConstructor(goog.ui.Dialog));
        36 * openConfirmDialog();
        37 * var lastDialogInstance = goog.ui.Dialog.getLastCall().getThis();
        38 * assertEquals('confirm', lastDialogInstance.getTitle());
        39 * }
        40 * </pre>
        41 *
        42 */
        43
        44goog.provide('goog.testing.FunctionCall');
        45goog.provide('goog.testing.recordConstructor');
        46goog.provide('goog.testing.recordFunction');
        47
        48goog.require('goog.testing.asserts');
        49
        50
        51/**
        52 * Wraps the function into another one which calls the inner function and
        53 * records its calls. The recorded function will have 3 static methods:
        54 * {@code getCallCount}, {@code getCalls} and {@code getLastCall} but won't
        55 * inherit the original function's prototype and static fields.
        56 *
        57 * @param {!Function=} opt_f The function to wrap and record. Defaults to
        58 * {@link goog.nullFunction}.
        59 * @return {!Function} The wrapped function.
        60 */
        61goog.testing.recordFunction = function(opt_f) {
        62 var f = opt_f || goog.nullFunction;
        63 var calls = [];
        64
        65 function recordedFunction() {
        66 try {
        67 var ret = f.apply(this, arguments);
        68 calls.push(new goog.testing.FunctionCall(f, this, arguments, ret, null));
        69 return ret;
        70 } catch (err) {
        71 calls.push(new goog.testing.FunctionCall(f, this, arguments, undefined,
        72 err));
        73 throw err;
        74 }
        75 }
        76
        77 /**
        78 * @return {number} Total number of calls.
        79 */
        80 recordedFunction.getCallCount = function() {
        81 return calls.length;
        82 };
        83
        84 /**
        85 * Asserts that the function was called {@code expected} times.
        86 * @param {number} expected The expected number of calls.
        87 */
        88 recordedFunction.assertCallCount = function(expected) {
        89 var actual = calls.length;
        90 assertEquals(
        91 'Expected ' + expected + ' call(s), but was ' + actual + '.',
        92 expected, actual);
        93 };
        94
        95 /**
        96 * @return {!Array<!goog.testing.FunctionCall>} All calls of the recorded
        97 * function.
        98 */
        99 recordedFunction.getCalls = function() {
        100 return calls;
        101 };
        102
        103
        104 /**
        105 * @return {goog.testing.FunctionCall} Last call of the recorded function or
        106 * null if it hasn't been called.
        107 */
        108 recordedFunction.getLastCall = function() {
        109 return calls[calls.length - 1] || null;
        110 };
        111
        112 /**
        113 * Returns and removes the last call of the recorded function.
        114 * @return {goog.testing.FunctionCall} Last call of the recorded function or
        115 * null if it hasn't been called.
        116 */
        117 recordedFunction.popLastCall = function() {
        118 return calls.pop() || null;
        119 };
        120
        121 /**
        122 * Resets the recorded function and removes all calls.
        123 */
        124 recordedFunction.reset = function() {
        125 calls.length = 0;
        126 };
        127
        128 return recordedFunction;
        129};
        130
        131
        132/**
        133 * Same as {@link goog.testing.recordFunction} but the recorded function will
        134 * have the same prototype and static fields as the original one. It can be
        135 * used with constructors.
        136 *
        137 * @param {!Function} ctor The function to wrap and record.
        138 * @return {!Function} The wrapped function.
        139 */
        140goog.testing.recordConstructor = function(ctor) {
        141 var recordedConstructor = goog.testing.recordFunction(ctor);
        142 recordedConstructor.prototype = ctor.prototype;
        143 goog.mixin(recordedConstructor, ctor);
        144 return recordedConstructor;
        145};
        146
        147
        148
        149/**
        150 * Struct for a single function call.
        151 * @param {!Function} func The called function.
        152 * @param {!Object} thisContext {@code this} context of called function.
        153 * @param {!Arguments} args Arguments of the called function.
        154 * @param {*} ret Return value of the function or undefined in case of error.
        155 * @param {*} error The error thrown by the function or null if none.
        156 * @constructor
        157 */
        158goog.testing.FunctionCall = function(func, thisContext, args, ret, error) {
        159 this.function_ = func;
        160 this.thisContext_ = thisContext;
        161 this.arguments_ = Array.prototype.slice.call(args);
        162 this.returnValue_ = ret;
        163 this.error_ = error;
        164};
        165
        166
        167/**
        168 * @return {!Function} The called function.
        169 */
        170goog.testing.FunctionCall.prototype.getFunction = function() {
        171 return this.function_;
        172};
        173
        174
        175/**
        176 * @return {!Object} {@code this} context of called function. It is the same as
        177 * the created object if the function is a constructor.
        178 */
        179goog.testing.FunctionCall.prototype.getThis = function() {
        180 return this.thisContext_;
        181};
        182
        183
        184/**
        185 * @return {!Array<?>} Arguments of the called function.
        186 */
        187goog.testing.FunctionCall.prototype.getArguments = function() {
        188 return this.arguments_;
        189};
        190
        191
        192/**
        193 * Returns the nth argument of the called function.
        194 * @param {number} index 0-based index of the argument.
        195 * @return {*} The argument value or undefined if there is no such argument.
        196 */
        197goog.testing.FunctionCall.prototype.getArgument = function(index) {
        198 return this.arguments_[index];
        199};
        200
        201
        202/**
        203 * @return {*} Return value of the function or undefined in case of error.
        204 */
        205goog.testing.FunctionCall.prototype.getReturnValue = function() {
        206 return this.returnValue_;
        207};
        208
        209
        210/**
        211 * @return {*} The error thrown by the function or null if none.
        212 */
        213goog.testing.FunctionCall.prototype.getError = function() {
        214 return this.error_;
        215};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/stacktrace.js.src.html b/docs/source/lib/goog/testing/stacktrace.js.src.html new file mode 100644 index 0000000..5fdefd0 --- /dev/null +++ b/docs/source/lib/goog/testing/stacktrace.js.src.html @@ -0,0 +1 @@ +stacktrace.js

        lib/goog/testing/stacktrace.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 Tools for parsing and pretty printing error stack traces.
        17 *
        18 */
        19
        20goog.provide('goog.testing.stacktrace');
        21goog.provide('goog.testing.stacktrace.Frame');
        22
        23
        24
        25/**
        26 * Class representing one stack frame.
        27 * @param {string} context Context object, empty in case of global functions or
        28 * if the browser doesn't provide this information.
        29 * @param {string} name Function name, empty in case of anonymous functions.
        30 * @param {string} alias Alias of the function if available. For example the
        31 * function name will be 'c' and the alias will be 'b' if the function is
        32 * defined as <code>a.b = function c() {};</code>.
        33 * @param {string} args Arguments of the function in parentheses if available.
        34 * @param {string} path File path or URL including line number and optionally
        35 * column number separated by colons.
        36 * @constructor
        37 * @final
        38 */
        39goog.testing.stacktrace.Frame = function(context, name, alias, args, path) {
        40 this.context_ = context;
        41 this.name_ = name;
        42 this.alias_ = alias;
        43 this.args_ = args;
        44 this.path_ = path;
        45};
        46
        47
        48/**
        49 * @return {string} The function name or empty string if the function is
        50 * anonymous and the object field which it's assigned to is unknown.
        51 */
        52goog.testing.stacktrace.Frame.prototype.getName = function() {
        53 return this.name_;
        54};
        55
        56
        57/**
        58 * @return {boolean} Whether the stack frame contains an anonymous function.
        59 */
        60goog.testing.stacktrace.Frame.prototype.isAnonymous = function() {
        61 return !this.name_ || this.context_ == '[object Object]';
        62};
        63
        64
        65/**
        66 * Brings one frame of the stack trace into a common format across browsers.
        67 * @return {string} Pretty printed stack frame.
        68 */
        69goog.testing.stacktrace.Frame.prototype.toCanonicalString = function() {
        70 var htmlEscape = goog.testing.stacktrace.htmlEscape_;
        71 var deobfuscate = goog.testing.stacktrace.maybeDeobfuscateFunctionName_;
        72
        73 var canonical = [
        74 this.context_ ? htmlEscape(this.context_) + '.' : '',
        75 this.name_ ? htmlEscape(deobfuscate(this.name_)) : 'anonymous',
        76 htmlEscape(this.args_),
        77 this.alias_ ? ' [as ' + htmlEscape(deobfuscate(this.alias_)) + ']' : ''
        78 ];
        79
        80 if (this.path_) {
        81 canonical.push(' at ');
        82 canonical.push(htmlEscape(this.path_));
        83 }
        84 return canonical.join('');
        85};
        86
        87
        88/**
        89 * Maximum number of steps while the call chain is followed.
        90 * @private {number}
        91 * @const
        92 */
        93goog.testing.stacktrace.MAX_DEPTH_ = 20;
        94
        95
        96/**
        97 * Maximum length of a string that can be matched with a RegExp on
        98 * Firefox 3x. Exceeding this approximate length will cause string.match
        99 * to exceed Firefox's stack quota. This situation can be encountered
        100 * when goog.globalEval is invoked with a long argument; such as
        101 * when loading a module.
        102 * @private {number}
        103 * @const
        104 */
        105goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_ = 500000;
        106
        107
        108/**
        109 * RegExp pattern for JavaScript identifiers. We don't support Unicode
        110 * identifiers defined in ECMAScript v3.
        111 * @private {string}
        112 * @const
        113 */
        114goog.testing.stacktrace.IDENTIFIER_PATTERN_ = '[a-zA-Z_$][\\w$]*';
        115
        116
        117/**
        118 * RegExp pattern for function name alias in the V8 stack trace.
        119 * @private {string}
        120 * @const
        121 */
        122goog.testing.stacktrace.V8_ALIAS_PATTERN_ =
        123 '(?: \\[as (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')\\])?';
        124
        125
        126/**
        127 * RegExp pattern for the context of a function call in a V8 stack trace.
        128 * Creates an optional submatch for the namespace identifier including the
        129 * "new" keyword for constructor calls (e.g. "new foo.Bar").
        130 * @private {string}
        131 * @const
        132 */
        133goog.testing.stacktrace.V8_CONTEXT_PATTERN_ =
        134 '(?:((?:new )?(?:\\[object Object\\]|' +
        135 goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
        136 '(?:\\.' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*))\\.)?';
        137
        138
        139/**
        140 * RegExp pattern for function names and constructor calls in the V8 stack
        141 * trace.
        142 * @private {string}
        143 * @const
        144 */
        145goog.testing.stacktrace.V8_FUNCTION_NAME_PATTERN_ =
        146 '(?:new )?(?:' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
        147 '|<anonymous>)';
        148
        149
        150/**
        151 * RegExp pattern for function call in the V8 stack trace. Creates 3 submatches
        152 * with context object (optional), function name and function alias (optional).
        153 * @private {string}
        154 * @const
        155 */
        156goog.testing.stacktrace.V8_FUNCTION_CALL_PATTERN_ =
        157 ' ' + goog.testing.stacktrace.V8_CONTEXT_PATTERN_ +
        158 '(' + goog.testing.stacktrace.V8_FUNCTION_NAME_PATTERN_ + ')' +
        159 goog.testing.stacktrace.V8_ALIAS_PATTERN_;
        160
        161
        162/**
        163 * RegExp pattern for an URL + position inside the file.
        164 * @private {string}
        165 * @const
        166 */
        167goog.testing.stacktrace.URL_PATTERN_ =
        168 '((?:http|https|file)://[^\\s)]+|javascript:.*)';
        169
        170
        171/**
        172 * RegExp pattern for an URL + line number + column number in V8.
        173 * The URL is either in submatch 1 or submatch 2.
        174 * @private {string}
        175 * @const
        176 */
        177goog.testing.stacktrace.CHROME_URL_PATTERN_ = ' (?:' +
        178 '\\(unknown source\\)' + '|' +
        179 '\\(native\\)' + '|' +
        180 '\\((.+)\\)|(.+))';
        181
        182
        183/**
        184 * Regular expression for parsing one stack frame in V8. For more information
        185 * on V8 stack frame formats, see
        186 * https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi.
        187 * @private {!RegExp}
        188 * @const
        189 */
        190goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_ = new RegExp('^ at' +
        191 '(?:' + goog.testing.stacktrace.V8_FUNCTION_CALL_PATTERN_ + ')?' +
        192 goog.testing.stacktrace.CHROME_URL_PATTERN_ + '$');
        193
        194
        195/**
        196 * RegExp pattern for function call in the Firefox stack trace.
        197 * Creates 2 submatches with function name (optional) and arguments.
        198 * @private {string}
        199 * @const
        200 */
        201goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ =
        202 '(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')?' +
        203 '(\\(.*\\))?@';
        204
        205
        206/**
        207 * Regular expression for parsing one stack frame in Firefox.
        208 * @private {!RegExp}
        209 * @const
        210 */
        211goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_ = new RegExp('^' +
        212 goog.testing.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ +
        213 '(?::0|' + goog.testing.stacktrace.URL_PATTERN_ + ')$');
        214
        215
        216/**
        217 * RegExp pattern for an anonymous function call in an Opera stack frame.
        218 * Creates 2 (optional) submatches: the context object and function name.
        219 * @private {string}
        220 * @const
        221 */
        222goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ =
        223 '<anonymous function(?:\\: ' +
        224 '(?:(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ +
        225 '(?:\\.' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')*)\\.)?' +
        226 '(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '))?>';
        227
        228
        229/**
        230 * RegExp pattern for a function call in an Opera stack frame.
        231 * Creates 4 (optional) submatches: the function name (if not anonymous),
        232 * the aliased context object and function name (if anonymous), and the
        233 * function call arguments.
        234 * @private {string}
        235 * @const
        236 */
        237goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ =
        238 '(?:(?:(' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')|' +
        239 goog.testing.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ +
        240 ')(\\(.*\\)))?@';
        241
        242
        243/**
        244 * Regular expression for parsing on stack frame in Opera 11.68 - 12.17.
        245 * Newer versions of Opera use V8 and stack frames should match against
        246 * goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_.
        247 * @private {!RegExp}
        248 * @const
        249 */
        250goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_ = new RegExp('^' +
        251 goog.testing.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ +
        252 goog.testing.stacktrace.URL_PATTERN_ + '?$');
        253
        254
        255/**
        256 * Regular expression for finding the function name in its source.
        257 * @private {!RegExp}
        258 * @const
        259 */
        260goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_ = new RegExp(
        261 '^function (' + goog.testing.stacktrace.IDENTIFIER_PATTERN_ + ')');
        262
        263
        264/**
        265 * RegExp pattern for function call in a IE stack trace. This expression allows
        266 * for identifiers like 'Anonymous function', 'eval code', and 'Global code'.
        267 * @private {string}
        268 * @const
        269 */
        270goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ = '(' +
        271 goog.testing.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)';
        272
        273
        274/**
        275 * Regular expression for parsing a stack frame in IE.
        276 * @private {!RegExp}
        277 * @const
        278 */
        279goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_ = new RegExp('^ at ' +
        280 goog.testing.stacktrace.IE_FUNCTION_CALL_PATTERN_ +
        281 '\\s*\\((eval code:[^)]*|' + goog.testing.stacktrace.URL_PATTERN_ +
        282 ')\\)?$');
        283
        284
        285/**
        286 * Creates a stack trace by following the call chain. Based on
        287 * {@link goog.debug.getStacktrace}.
        288 * @return {!Array<!goog.testing.stacktrace.Frame>} Stack frames.
        289 * @private
        290 * @suppress {es5Strict}
        291 */
        292goog.testing.stacktrace.followCallChain_ = function() {
        293 var frames = [];
        294 var fn = arguments.callee.caller;
        295 var depth = 0;
        296
        297 while (fn && depth < goog.testing.stacktrace.MAX_DEPTH_) {
        298 var fnString = Function.prototype.toString.call(fn);
        299 var match = fnString.match(goog.testing.stacktrace.FUNCTION_SOURCE_REGEXP_);
        300 var functionName = match ? match[1] : '';
        301
        302 var argsBuilder = ['('];
        303 if (fn.arguments) {
        304 for (var i = 0; i < fn.arguments.length; i++) {
        305 var arg = fn.arguments[i];
        306 if (i > 0) {
        307 argsBuilder.push(', ');
        308 }
        309 if (goog.isString(arg)) {
        310 argsBuilder.push('"', arg, '"');
        311 } else {
        312 // Some args are mocks, and we don't want to fail from them not having
        313 // expected a call to toString, so instead insert a static string.
        314 if (arg && arg['$replay']) {
        315 argsBuilder.push('goog.testing.Mock');
        316 } else {
        317 argsBuilder.push(String(arg));
        318 }
        319 }
        320 }
        321 } else {
        322 // Opera 10 doesn't know the arguments of native functions.
        323 argsBuilder.push('unknown');
        324 }
        325 argsBuilder.push(')');
        326 var args = argsBuilder.join('');
        327
        328 frames.push(new goog.testing.stacktrace.Frame('', functionName, '', args,
        329 ''));
        330
        331 /** @preserveTry */
        332 try {
        333 fn = fn.caller;
        334 } catch (e) {
        335 break;
        336 }
        337 depth++;
        338 }
        339
        340 return frames;
        341};
        342
        343
        344/**
        345 * Parses one stack frame.
        346 * @param {string} frameStr The stack frame as string.
        347 * @return {goog.testing.stacktrace.Frame} Stack frame object or null if the
        348 * parsing failed.
        349 * @private
        350 */
        351goog.testing.stacktrace.parseStackFrame_ = function(frameStr) {
        352 // This match includes newer versions of Opera (15+).
        353 var m = frameStr.match(goog.testing.stacktrace.V8_STACK_FRAME_REGEXP_);
        354 if (m) {
        355 return new goog.testing.stacktrace.Frame(m[1] || '', m[2] || '', m[3] || '',
        356 '', m[4] || m[5] || m[6] || '');
        357 }
        358
        359 if (frameStr.length >
        360 goog.testing.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_) {
        361 return goog.testing.stacktrace.parseLongFirefoxFrame_(frameStr);
        362 }
        363
        364 m = frameStr.match(goog.testing.stacktrace.FIREFOX_STACK_FRAME_REGEXP_);
        365 if (m) {
        366 return new goog.testing.stacktrace.Frame('', m[1] || '', '', m[2] || '',
        367 m[3] || '');
        368 }
        369
        370 // Match against Presto Opera 11.68 - 12.17.
        371 m = frameStr.match(goog.testing.stacktrace.OPERA_STACK_FRAME_REGEXP_);
        372 if (m) {
        373 return new goog.testing.stacktrace.Frame(m[2] || '', m[1] || m[3] || '',
        374 '', m[4] || '', m[5] || '');
        375 }
        376
        377 m = frameStr.match(goog.testing.stacktrace.IE_STACK_FRAME_REGEXP_);
        378 if (m) {
        379 return new goog.testing.stacktrace.Frame('', m[1] || '', '', '',
        380 m[2] || '');
        381 }
        382
        383 return null;
        384};
        385
        386
        387/**
        388 * Parses a long firefox stack frame.
        389 * @param {string} frameStr The stack frame as string.
        390 * @return {!goog.testing.stacktrace.Frame} Stack frame object.
        391 * @private
        392 */
        393goog.testing.stacktrace.parseLongFirefoxFrame_ = function(frameStr) {
        394 var firstParen = frameStr.indexOf('(');
        395 var lastAmpersand = frameStr.lastIndexOf('@');
        396 var lastColon = frameStr.lastIndexOf(':');
        397 var functionName = '';
        398 if ((firstParen >= 0) && (firstParen < lastAmpersand)) {
        399 functionName = frameStr.substring(0, firstParen);
        400 }
        401 var loc = '';
        402 if ((lastAmpersand >= 0) && (lastAmpersand + 1 < lastColon)) {
        403 loc = frameStr.substring(lastAmpersand + 1);
        404 }
        405 var args = '';
        406 if ((firstParen >= 0 && lastAmpersand > 0) &&
        407 (firstParen < lastAmpersand)) {
        408 args = frameStr.substring(firstParen, lastAmpersand);
        409 }
        410 return new goog.testing.stacktrace.Frame('', functionName, '', args, loc);
        411};
        412
        413
        414/**
        415 * Function to deobfuscate function names.
        416 * @type {function(string): string}
        417 * @private
        418 */
        419goog.testing.stacktrace.deobfuscateFunctionName_;
        420
        421
        422/**
        423 * Sets function to deobfuscate function names.
        424 * @param {function(string): string} fn function to deobfuscate function names.
        425 */
        426goog.testing.stacktrace.setDeobfuscateFunctionName = function(fn) {
        427 goog.testing.stacktrace.deobfuscateFunctionName_ = fn;
        428};
        429
        430
        431/**
        432 * Deobfuscates a compiled function name with the function passed to
        433 * {@link #setDeobfuscateFunctionName}. Returns the original function name if
        434 * the deobfuscator hasn't been set.
        435 * @param {string} name The function name to deobfuscate.
        436 * @return {string} The deobfuscated function name.
        437 * @private
        438 */
        439goog.testing.stacktrace.maybeDeobfuscateFunctionName_ = function(name) {
        440 return goog.testing.stacktrace.deobfuscateFunctionName_ ?
        441 goog.testing.stacktrace.deobfuscateFunctionName_(name) : name;
        442};
        443
        444
        445/**
        446 * Escapes the special character in HTML.
        447 * @param {string} text Plain text.
        448 * @return {string} Escaped text.
        449 * @private
        450 */
        451goog.testing.stacktrace.htmlEscape_ = function(text) {
        452 return text.replace(/&/g, '&amp;').
        453 replace(/</g, '&lt;').
        454 replace(/>/g, '&gt;').
        455 replace(/"/g, '&quot;');
        456};
        457
        458
        459/**
        460 * Converts the stack frames into canonical format. Chops the beginning and the
        461 * end of it which come from the testing environment, not from the test itself.
        462 * @param {!Array<goog.testing.stacktrace.Frame>} frames The frames.
        463 * @return {string} Canonical, pretty printed stack trace.
        464 * @private
        465 */
        466goog.testing.stacktrace.framesToString_ = function(frames) {
        467 // Removes the anonymous calls from the end of the stack trace (they come
        468 // from testrunner.js, testcase.js and asserts.js), so the stack trace will
        469 // end with the test... method.
        470 var lastIndex = frames.length - 1;
        471 while (frames[lastIndex] && frames[lastIndex].isAnonymous()) {
        472 lastIndex--;
        473 }
        474
        475 // Removes the beginning of the stack trace until the call of the private
        476 // _assert function (inclusive), so the stack trace will begin with a public
        477 // asserter. Does nothing if _assert is not present in the stack trace.
        478 var privateAssertIndex = -1;
        479 for (var i = 0; i < frames.length; i++) {
        480 if (frames[i] && frames[i].getName() == '_assert') {
        481 privateAssertIndex = i;
        482 break;
        483 }
        484 }
        485
        486 var canonical = [];
        487 for (var i = privateAssertIndex + 1; i <= lastIndex; i++) {
        488 canonical.push('> ');
        489 if (frames[i]) {
        490 canonical.push(frames[i].toCanonicalString());
        491 } else {
        492 canonical.push('(unknown)');
        493 }
        494 canonical.push('\n');
        495 }
        496 return canonical.join('');
        497};
        498
        499
        500/**
        501 * Parses the browser's native stack trace.
        502 * @param {string} stack Stack trace.
        503 * @return {!Array<goog.testing.stacktrace.Frame>} Stack frames. The
        504 * unrecognized frames will be nulled out.
        505 * @private
        506 */
        507goog.testing.stacktrace.parse_ = function(stack) {
        508 var lines = stack.replace(/\s*$/, '').split('\n');
        509 var frames = [];
        510 for (var i = 0; i < lines.length; i++) {
        511 frames.push(goog.testing.stacktrace.parseStackFrame_(lines[i]));
        512 }
        513 return frames;
        514};
        515
        516
        517/**
        518 * Brings the stack trace into a common format across browsers.
        519 * @param {string} stack Browser-specific stack trace.
        520 * @return {string} Same stack trace in common format.
        521 */
        522goog.testing.stacktrace.canonicalize = function(stack) {
        523 var frames = goog.testing.stacktrace.parse_(stack);
        524 return goog.testing.stacktrace.framesToString_(frames);
        525};
        526
        527
        528/**
        529 * Returns the native stack trace.
        530 * @return {string|!Array<!CallSite>}
        531 * @private
        532 */
        533goog.testing.stacktrace.getNativeStack_ = function() {
        534 var tmpError = new Error();
        535 if (tmpError.stack) {
        536 return tmpError.stack;
        537 }
        538
        539 // IE10 will only create a stack trace when the Error is thrown.
        540 // We use null.x() to throw an exception because the closure compiler may
        541 // replace "throw" with a function call in an attempt to minimize the binary
        542 // size, which in turn has the side effect of adding an unwanted stack frame.
        543 try {
        544 null.x();
        545 } catch (e) {
        546 return e.stack;
        547 }
        548 return '';
        549};
        550
        551
        552/**
        553 * Gets the native stack trace if available otherwise follows the call chain.
        554 * @return {string} The stack trace in canonical format.
        555 */
        556goog.testing.stacktrace.get = function() {
        557 var stack = goog.testing.stacktrace.getNativeStack_();
        558 var frames;
        559 if (!stack) {
        560 frames = goog.testing.stacktrace.followCallChain_();
        561 } else if (goog.isArray(stack)) {
        562 frames = goog.testing.stacktrace.callSitesToFrames_(stack);
        563 } else {
        564 frames = goog.testing.stacktrace.parse_(stack);
        565 }
        566 return goog.testing.stacktrace.framesToString_(frames);
        567};
        568
        569
        570/**
        571 * Converts an array of CallSite (elements of a stack trace in V8) to an array
        572 * of Frames.
        573 * @param {!Array<!CallSite>} stack The stack as an array of CallSites.
        574 * @return {!Array<!goog.testing.stacktrace.Frame>} The stack as an array of
        575 * Frames.
        576 * @private
        577 */
        578goog.testing.stacktrace.callSitesToFrames_ = function(stack) {
        579 var frames = [];
        580 for (var i = 0; i < stack.length; i++) {
        581 var callSite = stack[i];
        582 var functionName = callSite.getFunctionName() || 'unknown';
        583 var fileName = callSite.getFileName();
        584 var path = fileName ? fileName + ':' + callSite.getLineNumber() + ':' +
        585 callSite.getColumnNumber() : 'unknown';
        586 frames.push(
        587 new goog.testing.stacktrace.Frame('', functionName, '', '', path));
        588 }
        589 return frames;
        590};
        591
        592
        593goog.exportSymbol('setDeobfuscateFunctionName',
        594 goog.testing.stacktrace.setDeobfuscateFunctionName);
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/strictmock.js.src.html b/docs/source/lib/goog/testing/strictmock.js.src.html new file mode 100644 index 0000000..755a5ca --- /dev/null +++ b/docs/source/lib/goog/testing/strictmock.js.src.html @@ -0,0 +1 @@ +strictmock.js

        lib/goog/testing/strictmock.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 This file defines a strict mock implementation.
        17 */
        18
        19goog.provide('goog.testing.StrictMock');
        20
        21goog.require('goog.array');
        22goog.require('goog.testing.Mock');
        23
        24
        25
        26/**
        27 * This is a mock that verifies that methods are called in the order that they
        28 * are specified during the recording phase. Since it verifies order, it
        29 * follows 'fail fast' semantics. If it detects a deviation from the
        30 * expectations, it will throw an exception and not wait for verify to be
        31 * called.
        32 * @param {Object|Function} objectToMock The object that should be mocked, or
        33 * the constructor of an object to mock.
        34 * @param {boolean=} opt_mockStaticMethods An optional argument denoting that
        35 * a mock should be constructed from the static functions of a class.
        36 * @param {boolean=} opt_createProxy An optional argument denoting that
        37 * a proxy for the target mock should be created.
        38 * @constructor
        39 * @extends {goog.testing.Mock}
        40 * @final
        41 */
        42goog.testing.StrictMock = function(objectToMock, opt_mockStaticMethods,
        43 opt_createProxy) {
        44 goog.testing.Mock.call(this, objectToMock, opt_mockStaticMethods,
        45 opt_createProxy);
        46
        47 /**
        48 * An array of MockExpectations.
        49 * @type {Array<goog.testing.MockExpectation>}
        50 * @private
        51 */
        52 this.$expectations_ = [];
        53};
        54goog.inherits(goog.testing.StrictMock, goog.testing.Mock);
        55
        56
        57/** @override */
        58goog.testing.StrictMock.prototype.$recordExpectation = function() {
        59 this.$expectations_.push(this.$pendingExpectation);
        60};
        61
        62
        63/** @override */
        64goog.testing.StrictMock.prototype.$recordCall = function(name, args) {
        65 if (this.$expectations_.length == 0) {
        66 this.$throwCallException(name, args);
        67 }
        68
        69 // If the current expectation has a different name, make sure it was called
        70 // enough and then discard it. We're through with it.
        71 var currentExpectation = this.$expectations_[0];
        72 while (!this.$verifyCall(currentExpectation, name, args)) {
        73
        74 // This might be an item which has passed its min, and we can now
        75 // look past it, or it might be below its min and generate an error.
        76 if (currentExpectation.actualCalls < currentExpectation.minCalls) {
        77 this.$throwCallException(name, args, currentExpectation);
        78 }
        79
        80 this.$expectations_.shift();
        81 if (this.$expectations_.length < 1) {
        82 // Nothing left, but this may be a failed attempt to call the previous
        83 // item on the list, which may have been between its min and max.
        84 this.$throwCallException(name, args, currentExpectation);
        85 }
        86 currentExpectation = this.$expectations_[0];
        87 }
        88
        89 if (currentExpectation.maxCalls == 0) {
        90 this.$throwCallException(name, args);
        91 }
        92
        93 currentExpectation.actualCalls++;
        94 // If we hit the max number of calls for this expectation, we're finished
        95 // with it.
        96 if (currentExpectation.actualCalls == currentExpectation.maxCalls) {
        97 this.$expectations_.shift();
        98 }
        99
        100 return this.$do(currentExpectation, args);
        101};
        102
        103
        104/** @override */
        105goog.testing.StrictMock.prototype.$reset = function() {
        106 goog.testing.StrictMock.superClass_.$reset.call(this);
        107
        108 goog.array.clear(this.$expectations_);
        109};
        110
        111
        112/** @override */
        113goog.testing.StrictMock.prototype.$verify = function() {
        114 goog.testing.StrictMock.superClass_.$verify.call(this);
        115
        116 while (this.$expectations_.length > 0) {
        117 var expectation = this.$expectations_[0];
        118 if (expectation.actualCalls < expectation.minCalls) {
        119 this.$throwException('Missing a call to ' + expectation.name +
        120 '\nExpected: ' + expectation.minCalls + ' but was: ' +
        121 expectation.actualCalls);
        122
        123 } else {
        124 // Don't need to check max, that's handled when the call is made
        125 this.$expectations_.shift();
        126 }
        127 }
        128};
        129
        130
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/testcase.js.src.html b/docs/source/lib/goog/testing/testcase.js.src.html new file mode 100644 index 0000000..3e72364 --- /dev/null +++ b/docs/source/lib/goog/testing/testcase.js.src.html @@ -0,0 +1 @@ +testcase.js

        lib/goog/testing/testcase.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 A class representing a set of test functions to be run.
        17 *
        18 * Testing code should not have dependencies outside of goog.testing so as to
        19 * reduce the chance of masking missing dependencies.
        20 *
        21 * This file does not compile correctly with --collapse_properties. Use
        22 * --property_renaming=ALL_UNQUOTED instead.
        23 *
        24 */
        25
        26goog.provide('goog.testing.TestCase');
        27goog.provide('goog.testing.TestCase.Error');
        28goog.provide('goog.testing.TestCase.Order');
        29goog.provide('goog.testing.TestCase.Result');
        30goog.provide('goog.testing.TestCase.Test');
        31
        32
        33goog.require('goog.Promise');
        34goog.require('goog.Thenable');
        35goog.require('goog.asserts');
        36goog.require('goog.dom.TagName');
        37goog.require('goog.object');
        38goog.require('goog.testing.asserts');
        39goog.require('goog.testing.stacktrace');
        40
        41
        42
        43/**
        44 * A class representing a JsUnit test case. A TestCase is made up of a number
        45 * of test functions which can be run. Individual test cases can override the
        46 * following functions to set up their test environment:
        47 * - runTests - completely override the test's runner
        48 * - setUpPage - called before any of the test functions are run
        49 * - tearDownPage - called after all tests are finished
        50 * - setUp - called before each of the test functions
        51 * - tearDown - called after each of the test functions
        52 * - shouldRunTests - called before a test run, all tests are skipped if it
        53 * returns false. Can be used to disable tests on browsers
        54 * where they aren't expected to pass.
        55 *
        56 * Use {@link #autoDiscoverLifecycle} and {@link #autoDiscoverTests}
        57 *
        58 * @param {string=} opt_name The name of the test case, defaults to
        59 * 'Untitled Test Case'.
        60 * @constructor
        61 */
        62goog.testing.TestCase = function(opt_name) {
        63 /**
        64 * A name for the test case.
        65 * @type {string}
        66 * @private
        67 */
        68 this.name_ = opt_name || 'Untitled Test Case';
        69
        70 /**
        71 * Array of test functions that can be executed.
        72 * @type {!Array<!goog.testing.TestCase.Test>}
        73 * @private
        74 */
        75 this.tests_ = [];
        76
        77 /**
        78 * Set of test names and/or indices to execute, or null if all tests should
        79 * be executed.
        80 *
        81 * Indices are included to allow automation tools to run a subset of the
        82 * tests without knowing the exact contents of the test file.
        83 *
        84 * Indices should only be used with SORTED ordering.
        85 *
        86 * Example valid values:
        87 * <ul>
        88 * <li>[testName]
        89 * <li>[testName1, testName2]
        90 * <li>[2] - will run the 3rd test in the order specified
        91 * <li>[1,3,5]
        92 * <li>[testName1, testName2, 3, 5] - will work
        93 * <ul>
        94 * @type {Object}
        95 * @private
        96 */
        97 this.testsToRun_ = null;
        98
        99 /**
        100 * The order to run the auto-discovered tests in.
        101 * @type {string}
        102 */
        103 this.order = goog.testing.TestCase.Order.SORTED;
        104
        105 /** @private {function(!goog.testing.TestCase.Result)} */
        106 this.runNextTestCallback_ = goog.nullFunction;
        107
        108 /**
        109 * The number of {@link runNextTest_} frames currently on the stack.
        110 * When this exceeds {@link MAX_STACK_DEPTH_}, test execution is rescheduled
        111 * for a later tick of the event loop.
        112 * @see {finishTestInvocation_}
        113 * @private {number}
        114 */
        115 this.depth_ = 0;
        116
        117 /** @private {goog.testing.TestCase.Test} */
        118 this.curTest_ = null;
        119
        120 /**
        121 * Object used to encapsulate the test results.
        122 * @type {!goog.testing.TestCase.Result}
        123 * @protected
        124 * @suppress {underscore|visibility}
        125 */
        126 this.result_ = new goog.testing.TestCase.Result(this);
        127
        128 /**
        129 * The maximum time in milliseconds a promise returned from a test function
        130 * may remain pending before the test fails due to timeout.
        131 * @type {number}
        132 */
        133 this.promiseTimeout = 1000; // 1s
        134};
        135
        136
        137/**
        138 * The order to run the auto-discovered tests.
        139 * @enum {string}
        140 */
        141goog.testing.TestCase.Order = {
        142 /**
        143 * This is browser dependent and known to be different in FF and Safari
        144 * compared to others.
        145 */
        146 NATURAL: 'natural',
        147
        148 /** Random order. */
        149 RANDOM: 'random',
        150
        151 /** Sorted based on the name. */
        152 SORTED: 'sorted'
        153};
        154
        155
        156/**
        157 * @return {string} The name of the test.
        158 */
        159goog.testing.TestCase.prototype.getName = function() {
        160 return this.name_;
        161};
        162
        163
        164/**
        165 * The maximum amount of time in milliseconds that the test case can take
        166 * before it is forced to yield and reschedule. This prevents the test runner
        167 * from blocking the browser and potentially hurting the test harness.
        168 * @type {number}
        169 */
        170goog.testing.TestCase.maxRunTime = 200;
        171
        172
        173/**
        174 * The maximum number of {@link runNextTest_} frames that can be on the stack
        175 * before the test case is forced to yield and reschedule. Although modern
        176 * browsers can handle thousands of stack frames, this is set conservatively
        177 * because maximum stack depth has never been standardized, and engine-specific
        178 * techniques like tail cail optimization can affect the exact depth.
        179 * @private @const
        180 */
        181goog.testing.TestCase.MAX_STACK_DEPTH_ = 50;
        182
        183
        184/**
        185 * Save a reference to {@code window.setTimeout}, so any code that overrides the
        186 * default behavior (the MockClock, for example) doesn't affect our runner.
        187 * @type {function((Function|string), number=, *=): number}
        188 * @private
        189 */
        190goog.testing.TestCase.protectedSetTimeout_ = goog.global.setTimeout;
        191
        192
        193/**
        194 * Save a reference to {@code window.clearTimeout}, so any code that overrides
        195 * the default behavior (e.g. MockClock) doesn't affect our runner.
        196 * @type {function((null|number|undefined)): void}
        197 * @private
        198 */
        199goog.testing.TestCase.protectedClearTimeout_ = goog.global.clearTimeout;
        200
        201
        202/**
        203 * Save a reference to {@code window.Date}, so any code that overrides
        204 * the default behavior doesn't affect our runner.
        205 * @type {function(new: Date)}
        206 * @private
        207 */
        208goog.testing.TestCase.protectedDate_ = Date;
        209
        210
        211/**
        212 * Saved string referencing goog.global.setTimeout's string serialization. IE
        213 * sometimes fails to uphold equality for setTimeout, but the string version
        214 * stays the same.
        215 * @type {string}
        216 * @private
        217 */
        218goog.testing.TestCase.setTimeoutAsString_ = String(goog.global.setTimeout);
        219
        220
        221/**
        222 * TODO(user) replace this with prototype.currentTest.
        223 * Name of the current test that is running, or null if none is running.
        224 * @type {?string}
        225 */
        226goog.testing.TestCase.currentTestName = null;
        227
        228
        229/**
        230 * Avoid a dependency on goog.userAgent and keep our own reference of whether
        231 * the browser is IE.
        232 * @type {boolean}
        233 */
        234goog.testing.TestCase.IS_IE = typeof opera == 'undefined' &&
        235 !!goog.global.navigator &&
        236 goog.global.navigator.userAgent.indexOf('MSIE') != -1;
        237
        238
        239/**
        240 * Exception object that was detected before a test runs.
        241 * @type {*}
        242 * @protected
        243 */
        244goog.testing.TestCase.prototype.exceptionBeforeTest;
        245
        246
        247/**
        248 * Whether the test case has ever tried to execute.
        249 * @type {boolean}
        250 */
        251goog.testing.TestCase.prototype.started = false;
        252
        253
        254/**
        255 * Whether the test case is running.
        256 * @type {boolean}
        257 */
        258goog.testing.TestCase.prototype.running = false;
        259
        260
        261/**
        262 * Timestamp for when the test was started.
        263 * @type {number}
        264 * @private
        265 */
        266goog.testing.TestCase.prototype.startTime_ = 0;
        267
        268
        269/**
        270 * Time since the last batch of tests was started, if batchTime exceeds
        271 * {@link #maxRunTime} a timeout will be used to stop the tests blocking the
        272 * browser and a new batch will be started.
        273 * @type {number}
        274 * @private
        275 */
        276goog.testing.TestCase.prototype.batchTime_ = 0;
        277
        278
        279/**
        280 * Pointer to the current test.
        281 * @type {number}
        282 * @private
        283 */
        284goog.testing.TestCase.prototype.currentTestPointer_ = 0;
        285
        286
        287/**
        288 * Optional callback that will be executed when the test has finalized.
        289 * @type {Function}
        290 * @private
        291 */
        292goog.testing.TestCase.prototype.onCompleteCallback_ = null;
        293
        294
        295/**
        296 * Adds a new test to the test case.
        297 * @param {!goog.testing.TestCase.Test} test The test to add.
        298 */
        299goog.testing.TestCase.prototype.add = function(test) {
        300 goog.asserts.assert(test);
        301 if (this.started) {
        302 throw Error('Tests cannot be added after execute() has been called. ' +
        303 'Test: ' + test.name);
        304 }
        305
        306 this.tests_.push(test);
        307};
        308
        309
        310/**
        311 * Creates and adds a new test.
        312 *
        313 * Convenience function to make syntax less awkward when not using automatic
        314 * test discovery.
        315 *
        316 * @param {string} name The test name.
        317 * @param {!Function} ref Reference to the test function.
        318 * @param {!Object=} opt_scope Optional scope that the test function should be
        319 * called in.
        320 */
        321goog.testing.TestCase.prototype.addNewTest = function(name, ref, opt_scope) {
        322 var test = new goog.testing.TestCase.Test(name, ref, opt_scope || this);
        323 this.add(test);
        324};
        325
        326
        327/**
        328 * Sets the tests.
        329 * @param {!Array<goog.testing.TestCase.Test>} tests A new test array.
        330 * @protected
        331 */
        332goog.testing.TestCase.prototype.setTests = function(tests) {
        333 this.tests_ = tests;
        334};
        335
        336
        337/**
        338 * Gets the tests.
        339 * @return {!Array<goog.testing.TestCase.Test>} The test array.
        340 */
        341goog.testing.TestCase.prototype.getTests = function() {
        342 return this.tests_;
        343};
        344
        345
        346/**
        347 * Returns the number of tests contained in the test case.
        348 * @return {number} The number of tests.
        349 */
        350goog.testing.TestCase.prototype.getCount = function() {
        351 return this.tests_.length;
        352};
        353
        354
        355/**
        356 * Returns the number of tests actually run in the test case, i.e. subtracting
        357 * any which are skipped.
        358 * @return {number} The number of un-ignored tests.
        359 */
        360goog.testing.TestCase.prototype.getActuallyRunCount = function() {
        361 return this.testsToRun_ ? goog.object.getCount(this.testsToRun_) : 0;
        362};
        363
        364
        365/**
        366 * Returns the current test and increments the pointer.
        367 * @return {goog.testing.TestCase.Test} The current test case.
        368 */
        369goog.testing.TestCase.prototype.next = function() {
        370 var test;
        371 while ((test = this.tests_[this.currentTestPointer_++])) {
        372 if (!this.testsToRun_ || this.testsToRun_[test.name] ||
        373 this.testsToRun_[this.currentTestPointer_ - 1]) {
        374 return test;
        375 }
        376 }
        377 return null;
        378};
        379
        380
        381/**
        382 * Resets the test case pointer, so that next returns the first test.
        383 */
        384goog.testing.TestCase.prototype.reset = function() {
        385 this.currentTestPointer_ = 0;
        386 this.result_ = new goog.testing.TestCase.Result(this);
        387};
        388
        389
        390/**
        391 * Sets the callback function that should be executed when the tests have
        392 * completed.
        393 * @param {Function} fn The callback function.
        394 */
        395goog.testing.TestCase.prototype.setCompletedCallback = function(fn) {
        396 this.onCompleteCallback_ = fn;
        397};
        398
        399
        400/**
        401 * @param {goog.testing.TestCase.Order} order The sort order for running tests.
        402 */
        403goog.testing.TestCase.prototype.setOrder = function(order) {
        404 this.order = order;
        405};
        406
        407
        408/**
        409 * @param {Object<string, boolean>} testsToRun Set of tests to run. Entries in
        410 * the set may be test names, like "testFoo", or numeric indicies. Only
        411 * tests identified by name or by index will be executed.
        412 */
        413goog.testing.TestCase.prototype.setTestsToRun = function(testsToRun) {
        414 this.testsToRun_ = testsToRun;
        415};
        416
        417
        418/**
        419 * Can be overridden in test classes to indicate whether the tests in a case
        420 * should be run in that particular situation. For example, this could be used
        421 * to stop tests running in a particular browser, where browser support for
        422 * the class under test was absent.
        423 * @return {boolean} Whether any of the tests in the case should be run.
        424 */
        425goog.testing.TestCase.prototype.shouldRunTests = function() {
        426 return true;
        427};
        428
        429
        430/**
        431 * Executes the tests, yielding asynchronously if execution time exceeds
        432 * {@link maxRunTime}. There is no guarantee that the test case has finished
        433 * once this method has returned. To be notified when the test case
        434 * has finished, use {@link #setCompletedCallback} or
        435 * {@link #runTestsReturningPromise}.
        436 */
        437goog.testing.TestCase.prototype.execute = function() {
        438 if (!this.prepareForRun_()) {
        439 return;
        440 }
        441 this.log('Starting tests: ' + this.name_);
        442 this.cycleTests();
        443};
        444
        445
        446/**
        447 * Sets up the internal state of the test case for a run.
        448 * @return {boolean} If false, preparation failed because the test case
        449 * is not supposed to run in the present environment.
        450 * @private
        451 */
        452goog.testing.TestCase.prototype.prepareForRun_ = function() {
        453 this.started = true;
        454 this.reset();
        455 this.startTime_ = this.now();
        456 this.running = true;
        457 this.result_.totalCount = this.getCount();
        458 if (!this.shouldRunTests()) {
        459 this.log('shouldRunTests() returned false, skipping these tests.');
        460 this.result_.testSuppressed = true;
        461 this.finalize();
        462 return false;
        463 }
        464 return true;
        465};
        466
        467
        468/**
        469 * Finalizes the test case, called when the tests have finished executing.
        470 */
        471goog.testing.TestCase.prototype.finalize = function() {
        472 this.saveMessage('Done');
        473
        474 this.tearDownPage();
        475
        476 var restoredSetTimeout =
        477 goog.testing.TestCase.protectedSetTimeout_ == goog.global.setTimeout &&
        478 goog.testing.TestCase.protectedClearTimeout_ == goog.global.clearTimeout;
        479 if (!restoredSetTimeout && goog.testing.TestCase.IS_IE &&
        480 String(goog.global.setTimeout) ==
        481 goog.testing.TestCase.setTimeoutAsString_) {
        482 // In strange cases, IE's value of setTimeout *appears* to change, but
        483 // the string representation stays stable.
        484 restoredSetTimeout = true;
        485 }
        486
        487 if (!restoredSetTimeout) {
        488 var message = 'ERROR: Test did not restore setTimeout and clearTimeout';
        489 this.saveMessage(message);
        490 var err = new goog.testing.TestCase.Error(this.name_, message);
        491 this.result_.errors.push(err);
        492 }
        493 goog.global.clearTimeout = goog.testing.TestCase.protectedClearTimeout_;
        494 goog.global.setTimeout = goog.testing.TestCase.protectedSetTimeout_;
        495 this.endTime_ = this.now();
        496 this.running = false;
        497 this.result_.runTime = this.endTime_ - this.startTime_;
        498 this.result_.numFilesLoaded = this.countNumFilesLoaded_();
        499 this.result_.complete = true;
        500
        501 this.log(this.result_.getSummary());
        502 if (this.result_.isSuccess()) {
        503 this.log('Tests complete');
        504 } else {
        505 this.log('Tests Failed');
        506 }
        507 if (this.onCompleteCallback_) {
        508 var fn = this.onCompleteCallback_;
        509 // Execute's the completed callback in the context of the global object.
        510 fn();
        511 this.onCompleteCallback_ = null;
        512 }
        513};
        514
        515
        516/**
        517 * Saves a message to the result set.
        518 * @param {string} message The message to save.
        519 */
        520goog.testing.TestCase.prototype.saveMessage = function(message) {
        521 this.result_.messages.push(this.getTimeStamp_() + ' ' + message);
        522};
        523
        524
        525/**
        526 * @return {boolean} Whether the test case is running inside the multi test
        527 * runner.
        528 */
        529goog.testing.TestCase.prototype.isInsideMultiTestRunner = function() {
        530 var top = goog.global['top'];
        531 return top && typeof top['_allTests'] != 'undefined';
        532};
        533
        534
        535/**
        536 * Logs an object to the console, if available.
        537 * @param {*} val The value to log. Will be ToString'd.
        538 */
        539goog.testing.TestCase.prototype.log = function(val) {
        540 if (!this.isInsideMultiTestRunner() && goog.global.console) {
        541 if (typeof val == 'string') {
        542 val = this.getTimeStamp_() + ' : ' + val;
        543 }
        544 if (val instanceof Error && val.stack) {
        545 // Chrome does console.log asynchronously in a different process
        546 // (http://code.google.com/p/chromium/issues/detail?id=50316).
        547 // This is an acute problem for Errors, which almost never survive.
        548 // Grab references to the immutable strings so they survive.
        549 goog.global.console.log(val, val.message, val.stack);
        550 // TODO(gboyer): Consider for Chrome cloning any object if we can ensure
        551 // there are no circular references.
        552 } else {
        553 goog.global.console.log(val);
        554 }
        555 }
        556};
        557
        558
        559/**
        560 * @return {boolean} Whether the test was a success.
        561 */
        562goog.testing.TestCase.prototype.isSuccess = function() {
        563 return !!this.result_ && this.result_.isSuccess();
        564};
        565
        566
        567/**
        568 * Returns a string detailing the results from the test.
        569 * @param {boolean=} opt_verbose If true results will include data about all
        570 * tests, not just what failed.
        571 * @return {string} The results from the test.
        572 */
        573goog.testing.TestCase.prototype.getReport = function(opt_verbose) {
        574 var rv = [];
        575
        576 if (this.running) {
        577 rv.push(this.name_ + ' [RUNNING]');
        578 } else {
        579 var label = this.result_.isSuccess() ? 'PASSED' : 'FAILED';
        580 rv.push(this.name_ + ' [' + label + ']');
        581 }
        582
        583 if (goog.global.location) {
        584 rv.push(this.trimPath_(goog.global.location.href));
        585 }
        586
        587 rv.push(this.result_.getSummary());
        588
        589 if (opt_verbose) {
        590 rv.push('.', this.result_.messages.join('\n'));
        591 } else if (!this.result_.isSuccess()) {
        592 rv.push(this.result_.errors.join('\n'));
        593 }
        594
        595 rv.push(' ');
        596
        597 return rv.join('\n');
        598};
        599
        600
        601/**
        602 * Returns the test results.
        603 * @return {!goog.testing.TestCase.Result}
        604 * @package
        605 */
        606goog.testing.TestCase.prototype.getResult = function() {
        607 return this.result_;
        608};
        609
        610
        611/**
        612 * Returns the amount of time it took for the test to run.
        613 * @return {number} The run time, in milliseconds.
        614 */
        615goog.testing.TestCase.prototype.getRunTime = function() {
        616 return this.result_.runTime;
        617};
        618
        619
        620/**
        621 * Returns the number of script files that were loaded in order to run the test.
        622 * @return {number} The number of script files.
        623 */
        624goog.testing.TestCase.prototype.getNumFilesLoaded = function() {
        625 return this.result_.numFilesLoaded;
        626};
        627
        628
        629/**
        630 * Returns the test results object: a map from test names to a list of test
        631 * failures (if any exist).
        632 * @return {!Object<string, !Array<string>>} Tests results object.
        633 */
        634goog.testing.TestCase.prototype.getTestResults = function() {
        635 return this.result_.resultsByName;
        636};
        637
        638
        639/**
        640 * Executes each of the tests, yielding asynchronously if execution time
        641 * exceeds {@link #maxRunTime}. There is no guarantee that the test case
        642 * has finished execution once this method has returned.
        643 * To be notified when the test case has finished execution, use
        644 * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
        645 *
        646 * Overridable by the individual test case. This allows test cases to defer
        647 * when the test is actually started. If overridden, finalize must be called
        648 * by the test to indicate it has finished.
        649 */
        650goog.testing.TestCase.prototype.runTests = function() {
        651 try {
        652 this.setUpPage();
        653 } catch (e) {
        654 this.exceptionBeforeTest = e;
        655 }
        656 this.execute();
        657};
        658
        659
        660/**
        661 * Executes each of the tests, returning a promise that resolves with the
        662 * test results once they are done running.
        663 * @return {!IThenable<!goog.testing.TestCase.Result>}
        664 * @final
        665 * @package
        666 */
        667goog.testing.TestCase.prototype.runTestsReturningPromise = function() {
        668 try {
        669 this.setUpPage();
        670 } catch (e) {
        671 this.exceptionBeforeTest = e;
        672 }
        673 if (!this.prepareForRun_()) {
        674 return goog.Promise.resolve(this.result_);
        675 }
        676 this.log('Starting tests: ' + this.name_);
        677 this.saveMessage('Start');
        678 this.batchTime_ = this.now();
        679 return new goog.Promise(function(resolve) {
        680 this.runNextTestCallback_ = resolve;
        681 this.runNextTest_();
        682 }, this);
        683};
        684
        685
        686/**
        687 * Executes the next test method synchronously or with promises, depending on
        688 * the test method's return value.
        689 *
        690 * If the test method returns a promise, the next test method will run once
        691 * the promise is resolved or rejected. If the test method does not
        692 * return a promise, it is assumed to be synchronous, and execution proceeds
        693 * immediately to the next test method. This means that test cases can run
        694 * partially synchronously and partially asynchronously, depending on
        695 * the return values of their test methods. In particular, a test case
        696 * executes synchronously until the first promise is returned from a
        697 * test method (or until a resource limit is reached; see
        698 * {@link finishTestInvocation_}).
        699 * @private
        700 */
        701goog.testing.TestCase.prototype.runNextTest_ = function() {
        702 this.curTest_ = this.next();
        703 if (!this.curTest_ || !this.running) {
        704 this.finalize();
        705 this.runNextTestCallback_(this.result_);
        706 return;
        707 }
        708 this.result_.runCount++;
        709 this.log('Running test: ' + this.curTest_.name);
        710 if (this.maybeFailTestEarly(this.curTest_)) {
        711 this.finishTestInvocation_();
        712 return;
        713 }
        714 goog.testing.TestCase.currentTestName = this.curTest_.name;
        715 this.invokeTestFunction_(
        716 this.setUp, this.safeRunTest_, this.safeTearDown_,
        717 'setUp');
        718};
        719
        720
        721/**
        722 * Calls the given test function, handling errors appropriately.
        723 * @private
        724 */
        725goog.testing.TestCase.prototype.safeRunTest_ = function() {
        726 this.invokeTestFunction_(
        727 goog.bind(this.curTest_.ref, this.curTest_.scope),
        728 this.safeTearDown_,
        729 this.safeTearDown_,
        730 this.curTest_.name);
        731};
        732
        733
        734/**
        735 * Calls {@link tearDown}, handling errors appropriately.
        736 * @param {*=} opt_error Error associated with the test, if any.
        737 * @private
        738 */
        739goog.testing.TestCase.prototype.safeTearDown_ = function(opt_error) {
        740 if (arguments.length == 1) {
        741 this.doError(this.curTest_, opt_error);
        742 }
        743 this.invokeTestFunction_(
        744 this.tearDown, this.finishTestInvocation_, this.finishTestInvocation_,
        745 'tearDown');
        746};
        747
        748
        749/**
        750 * Calls the given {@code fn}, then calls either {@code onSuccess} or
        751 * {@code onFailure}, either synchronously or using promises, depending on
        752 * {@code fn}'s return value.
        753 *
        754 * If {@code fn} throws an exception, {@code onFailure} is called immediately
        755 * with the exception.
        756 *
        757 * If {@code fn} returns a promise, and the promise is eventually resolved,
        758 * {@code onSuccess} is called with no arguments. If the promise is eventually
        759 * rejected, {@code onFailure} is called with the rejection reason.
        760 *
        761 * Otherwise, if {@code fn} neither returns a promise nor throws an exception,
        762 * {@code onSuccess} is called immediately with no arguments.
        763 *
        764 * {@code fn}, {@code onSuccess}, and {@code onFailure} are all called with
        765 * the TestCase instance as the method receiver.
        766 *
        767 * @param {function()} fn The function to call.
        768 * @param {function()} onSuccess Success callback.
        769 * @param {function(*)} onFailure Failure callback.
        770 * @param {string} fnName Name of the function being invoked e.g. 'setUp'.
        771 * @private
        772 */
        773goog.testing.TestCase.prototype.invokeTestFunction_ = function(
        774 fn, onSuccess, onFailure, fnName) {
        775 try {
        776 var retval = fn.call(this);
        777 if (goog.Thenable.isImplementedBy(retval) ||
        778 goog.isFunction(retval && retval['then'])) {
        779 var self = this;
        780 retval = this.rejectIfPromiseTimesOut_(
        781 retval, self.promiseTimeout,
        782 'Timed out while waiting for a promise returned from ' + fnName +
        783 ' to resolve. Set goog.testing.TestCase.getActiveTestCase()' +
        784 '.promiseTimeout to adjust the timeout.');
        785 retval.then(
        786 function() {
        787 self.resetBatchTimeAfterPromise_();
        788 onSuccess.call(self);
        789 },
        790 function(e) {
        791 self.resetBatchTimeAfterPromise_();
        792 onFailure.call(self, e);
        793 });
        794 } else {
        795 onSuccess.call(this);
        796 }
        797 } catch (e) {
        798 onFailure.call(this, e);
        799 }
        800};
        801
        802
        803/**
        804 * Resets the batch run timer. This should only be called after resolving a
        805 * promise since Promise.then() has an implicit yield.
        806 * @private
        807 */
        808goog.testing.TestCase.prototype.resetBatchTimeAfterPromise_ = function() {
        809 this.batchTime_ = this.now();
        810};
        811
        812
        813/**
        814 * Finishes up bookkeeping for the current test function, and schedules
        815 * the next test function to run, either immediately or asychronously.
        816 * @param {*=} opt_error Optional error resulting from the test invocation.
        817 * @private
        818 */
        819goog.testing.TestCase.prototype.finishTestInvocation_ = function(opt_error) {
        820 if (arguments.length == 1) {
        821 this.doError(this.curTest_, opt_error);
        822 }
        823
        824 // If no errors have been recorded for the test, it is a success.
        825 if (!(this.curTest_.name in this.result_.resultsByName) ||
        826 !this.result_.resultsByName[this.curTest_.name].length) {
        827 this.doSuccess(this.curTest_);
        828 }
        829
        830 goog.testing.TestCase.currentTestName = null;
        831
        832 // If the test case has consumed too much time or stack space,
        833 // yield to avoid blocking the browser. Otherwise, proceed to the next test.
        834 if (this.depth_ > goog.testing.TestCase.MAX_STACK_DEPTH_ ||
        835 this.now() - this.batchTime_ > goog.testing.TestCase.maxRunTime) {
        836 this.saveMessage('Breaking async');
        837 this.timeout(goog.bind(this.startNextBatch_, this), 0);
        838 } else {
        839 ++this.depth_;
        840 this.runNextTest_();
        841 }
        842};
        843
        844
        845/**
        846 * Start a new batch to tests after yielding, resetting batchTime and depth.
        847 * @private
        848 */
        849goog.testing.TestCase.prototype.startNextBatch_ = function() {
        850 this.batchTime_ = this.now();
        851 this.depth_ = 0;
        852 this.runNextTest_();
        853};
        854
        855
        856/**
        857 * Reorders the tests depending on the {@code order} field.
        858 * @private
        859 */
        860goog.testing.TestCase.prototype.orderTests_ = function() {
        861 switch (this.order) {
        862 case goog.testing.TestCase.Order.RANDOM:
        863 // Fisher-Yates shuffle
        864 var i = this.tests_.length;
        865 while (i > 1) {
        866 // goog.math.randomInt is inlined to reduce dependencies.
        867 var j = Math.floor(Math.random() * i); // exclusive
        868 i--;
        869 var tmp = this.tests_[i];
        870 this.tests_[i] = this.tests_[j];
        871 this.tests_[j] = tmp;
        872 }
        873 break;
        874
        875 case goog.testing.TestCase.Order.SORTED:
        876 this.tests_.sort(function(t1, t2) {
        877 if (t1.name == t2.name) {
        878 return 0;
        879 }
        880 return t1.name < t2.name ? -1 : 1;
        881 });
        882 break;
        883
        884 // Do nothing for NATURAL.
        885 }
        886};
        887
        888
        889/**
        890 * Gets list of objects that potentially contain test cases. For IE 8 and below,
        891 * this is the global "this" (for properties set directly on the global this or
        892 * window) and the RuntimeObject (for global variables and functions). For all
        893 * other browsers, the array simply contains the global this.
        894 *
        895 * @param {string=} opt_prefix An optional prefix. If specified, only get things
        896 * under this prefix. Note that the prefix is only honored in IE, since it
        897 * supports the RuntimeObject:
        898 * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
        899 * TODO: Remove this option.
        900 * @return {!Array<!Object>} A list of objects that should be inspected.
        901 */
        902goog.testing.TestCase.prototype.getGlobals = function(opt_prefix) {
        903 return goog.testing.TestCase.getGlobals(opt_prefix);
        904};
        905
        906
        907/**
        908 * Gets list of objects that potentially contain test cases. For IE 8 and below,
        909 * this is the global "this" (for properties set directly on the global this or
        910 * window) and the RuntimeObject (for global variables and functions). For all
        911 * other browsers, the array simply contains the global this.
        912 *
        913 * @param {string=} opt_prefix An optional prefix. If specified, only get things
        914 * under this prefix. Note that the prefix is only honored in IE, since it
        915 * supports the RuntimeObject:
        916 * http://msdn.microsoft.com/en-us/library/ff521039%28VS.85%29.aspx
        917 * TODO: Remove this option.
        918 * @return {!Array<!Object>} A list of objects that should be inspected.
        919 */
        920goog.testing.TestCase.getGlobals = function(opt_prefix) {
        921 // Look in the global scope for most browsers, on IE we use the little known
        922 // RuntimeObject which holds references to all globals. We reference this
        923 // via goog.global so that there isn't an aliasing that throws an exception
        924 // in Firefox.
        925 return typeof goog.global['RuntimeObject'] != 'undefined' ?
        926 [goog.global['RuntimeObject']((opt_prefix || '') + '*'), goog.global] :
        927 [goog.global];
        928};
        929
        930
        931/**
        932 * @return {?goog.testing.TestCase} currently active test case or null if not
        933 * test is currently running.
        934 */
        935goog.testing.TestCase.getActiveTestCase = function() {
        936 var gTestRunner = goog.global['G_testRunner'];
        937 if (gTestRunner && gTestRunner.testCase) {
        938 return gTestRunner.testCase;
        939 } else {
        940 return null;
        941 }
        942};
        943
        944
        945/**
        946 * Gets called before any tests are executed. Can be overridden to set up the
        947 * environment for the whole test case.
        948 */
        949goog.testing.TestCase.prototype.setUpPage = function() {};
        950
        951
        952/**
        953 * Gets called after all tests have been executed. Can be overridden to tear
        954 * down the entire test case.
        955 */
        956goog.testing.TestCase.prototype.tearDownPage = function() {};
        957
        958
        959/**
        960 * Gets called before every goog.testing.TestCase.Test is been executed. Can be
        961 * overridden to add set up functionality to each test.
        962 */
        963goog.testing.TestCase.prototype.setUp = function() {};
        964
        965
        966/**
        967 * Gets called after every goog.testing.TestCase.Test has been executed. Can be
        968 * overriden to add tear down functionality to each test.
        969 */
        970goog.testing.TestCase.prototype.tearDown = function() {};
        971
        972
        973/**
        974 * @return {string} The function name prefix used to auto-discover tests.
        975 */
        976goog.testing.TestCase.prototype.getAutoDiscoveryPrefix = function() {
        977 return 'test';
        978};
        979
        980
        981/**
        982 * @return {number} Time since the last batch of tests was started.
        983 * @protected
        984 */
        985goog.testing.TestCase.prototype.getBatchTime = function() {
        986 return this.batchTime_;
        987};
        988
        989
        990/**
        991 * @param {number} batchTime Time since the last batch of tests was started.
        992 * @protected
        993 */
        994goog.testing.TestCase.prototype.setBatchTime = function(batchTime) {
        995 this.batchTime_ = batchTime;
        996};
        997
        998
        999/**
        1000 * Creates a {@code goog.testing.TestCase.Test} from an auto-discovered
        1001 * function.
        1002 * @param {string} name The name of the function.
        1003 * @param {function() : void} ref The auto-discovered function.
        1004 * @return {!goog.testing.TestCase.Test} The newly created test.
        1005 * @protected
        1006 */
        1007goog.testing.TestCase.prototype.createTestFromAutoDiscoveredFunction =
        1008 function(name, ref) {
        1009 return new goog.testing.TestCase.Test(name, ref, goog.global);
        1010};
        1011
        1012
        1013/**
        1014 * Adds any functions defined on 'obj' (the global object, by default)
        1015 * that correspond to lifecycle events for the test case. Overrides
        1016 * setUp, tearDown, setUpPage, tearDownPage, runTests, and shouldRunTests
        1017 * if they are defined on 'obj'.
        1018 * @param {!Object=} opt_obj Defaults to goog.global.
        1019 */
        1020goog.testing.TestCase.prototype.autoDiscoverLifecycle = function(opt_obj) {
        1021 var obj = opt_obj || goog.global;
        1022 if (obj['setUp']) {
        1023 this.setUp = goog.bind(obj['setUp'], obj);
        1024 }
        1025 if (obj['tearDown']) {
        1026 this.tearDown = goog.bind(obj['tearDown'], obj);
        1027 }
        1028 if (obj['setUpPage']) {
        1029 this.setUpPage = goog.bind(obj['setUpPage'], obj);
        1030 }
        1031 if (obj['tearDownPage']) {
        1032 this.tearDownPage = goog.bind(obj['tearDownPage'], obj);
        1033 }
        1034 if (obj['runTests']) {
        1035 this.runTests = goog.bind(obj['runTests'], obj);
        1036 }
        1037 if (obj['shouldRunTests']) {
        1038 this.shouldRunTests = goog.bind(obj['shouldRunTests'], obj);
        1039 }
        1040};
        1041
        1042
        1043// TODO(johnlenz): make this package private
        1044/**
        1045 * @param {!Object} obj An object from which to extract test and lifecycle
        1046 * methods.
        1047 */
        1048goog.testing.TestCase.prototype.setTestObj = function(obj) {
        1049 // Drop any previously added (likely auto-discovered) tests, only one source
        1050 // of discovered test and life-cycle methods is allowed.
        1051 goog.asserts.assert(this.tests_.length == 0,
        1052 'Test methods have already been configured.');
        1053
        1054 var regex = new RegExp('^' + this.getAutoDiscoveryPrefix());
        1055 for (var name in obj) {
        1056 if (regex.test(name)) {
        1057 var testMethod = obj[name];
        1058 if (goog.isFunction(testMethod)) {
        1059 this.addNewTest(name, testMethod, obj);
        1060 }
        1061 }
        1062 }
        1063
        1064 this.autoDiscoverLifecycle(obj);
        1065};
        1066
        1067
        1068/**
        1069 * Adds any functions defined in the global scope that are prefixed with "test"
        1070 * to the test case.
        1071 */
        1072goog.testing.TestCase.prototype.autoDiscoverTests = function() {
        1073 var prefix = this.getAutoDiscoveryPrefix();
        1074 var testSources = this.getGlobals(prefix);
        1075
        1076 var foundTests = [];
        1077
        1078 for (var i = 0; i < testSources.length; i++) {
        1079 var testSource = testSources[i];
        1080 for (var name in testSource) {
        1081 if ((new RegExp('^' + prefix)).test(name)) {
        1082 var ref;
        1083 try {
        1084 ref = testSource[name];
        1085 } catch (ex) {
        1086 // NOTE(brenneman): When running tests from a file:// URL on Firefox
        1087 // 3.5 for Windows, any reference to goog.global.sessionStorage raises
        1088 // an "Operation is not supported" exception. Ignore any exceptions
        1089 // raised by simply accessing global properties.
        1090 ref = undefined;
        1091 }
        1092
        1093 if (goog.isFunction(ref)) {
        1094 foundTests.push(this.createTestFromAutoDiscoveredFunction(name, ref));
        1095 }
        1096 }
        1097 }
        1098 }
        1099
        1100 for (var i = 0; i < foundTests.length; i++) {
        1101 this.add(foundTests[i]);
        1102 }
        1103 this.orderTests_();
        1104
        1105 this.log(this.getCount() + ' tests auto-discovered');
        1106
        1107 // TODO(user): Do this as a separate call. Unfortunately, a lot of projects
        1108 // currently override autoDiscoverTests and expect lifecycle events to be
        1109 // registered as a part of this call.
        1110 this.autoDiscoverLifecycle();
        1111};
        1112
        1113
        1114/**
        1115 * Checks to see if the test should be marked as failed before it is run.
        1116 *
        1117 * If there was an error in setUpPage, we treat that as a failure for all tests
        1118 * and mark them all as having failed.
        1119 *
        1120 * @param {goog.testing.TestCase.Test} testCase The current test case.
        1121 * @return {boolean} Whether the test was marked as failed.
        1122 * @protected
        1123 */
        1124goog.testing.TestCase.prototype.maybeFailTestEarly = function(testCase) {
        1125 if (this.exceptionBeforeTest) {
        1126 // We just use the first error to report an error on a failed test.
        1127 testCase.name = 'setUpPage for ' + testCase.name;
        1128 this.doError(testCase, this.exceptionBeforeTest);
        1129 return true;
        1130 }
        1131 return false;
        1132};
        1133
        1134
        1135/**
        1136 * Cycles through the tests, yielding asynchronously if the execution time
        1137 * execeeds {@link #maxRunTime}. In particular, there is no guarantee that
        1138 * the test case has finished execution once this method has returned.
        1139 * To be notified when the test case has finished execution, use
        1140 * {@link #setCompletedCallback} or {@link #runTestsReturningPromise}.
        1141 */
        1142goog.testing.TestCase.prototype.cycleTests = function() {
        1143 this.saveMessage('Start');
        1144 this.batchTime_ = this.now();
        1145 if (this.running) {
        1146 this.runNextTestCallback_ = goog.nullFunction;
        1147 // Kick off the tests. runNextTest_ will schedule all of the tests,
        1148 // using a mixture of synchronous and asynchronous strategies.
        1149 this.runNextTest_();
        1150 }
        1151};
        1152
        1153
        1154/**
        1155 * Counts the number of files that were loaded for dependencies that are
        1156 * required to run the test.
        1157 * @return {number} The number of files loaded.
        1158 * @private
        1159 */
        1160goog.testing.TestCase.prototype.countNumFilesLoaded_ = function() {
        1161 var scripts = document.getElementsByTagName(goog.dom.TagName.SCRIPT);
        1162 var count = 0;
        1163 for (var i = 0, n = scripts.length; i < n; i++) {
        1164 if (scripts[i].src) {
        1165 count++;
        1166 }
        1167 }
        1168 return count;
        1169};
        1170
        1171
        1172/**
        1173 * Calls a function after a delay, using the protected timeout.
        1174 * @param {Function} fn The function to call.
        1175 * @param {number} time Delay in milliseconds.
        1176 * @return {number} The timeout id.
        1177 * @protected
        1178 */
        1179goog.testing.TestCase.prototype.timeout = function(fn, time) {
        1180 // NOTE: invoking protectedSetTimeout_ as a member of goog.testing.TestCase
        1181 // would result in an Illegal Invocation error. The method must be executed
        1182 // with the global context.
        1183 var protectedSetTimeout = goog.testing.TestCase.protectedSetTimeout_;
        1184 return protectedSetTimeout(fn, time);
        1185};
        1186
        1187
        1188/**
        1189 * Clears a timeout created by {@code this.timeout()}.
        1190 * @param {number} id A timeout id.
        1191 * @protected
        1192 */
        1193goog.testing.TestCase.prototype.clearTimeout = function(id) {
        1194 // NOTE: see execution note for protectedSetTimeout above.
        1195 var protectedClearTimeout = goog.testing.TestCase.protectedClearTimeout_;
        1196 protectedClearTimeout(id);
        1197};
        1198
        1199
        1200/**
        1201 * @return {number} The current time in milliseconds, don't use goog.now as some
        1202 * tests override it.
        1203 * @protected
        1204 */
        1205goog.testing.TestCase.prototype.now = function() {
        1206 // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
        1207 var protectedDate = goog.testing.TestCase.protectedDate_;
        1208 return new protectedDate().getTime();
        1209};
        1210
        1211
        1212/**
        1213 * Returns the current time.
        1214 * @return {string} HH:MM:SS.
        1215 * @private
        1216 */
        1217goog.testing.TestCase.prototype.getTimeStamp_ = function() {
        1218 // Cannot use "new goog.testing.TestCase.protectedDate_()" due to b/8323223.
        1219 var protectedDate = goog.testing.TestCase.protectedDate_;
        1220 var d = new protectedDate();
        1221
        1222 // Ensure millis are always 3-digits
        1223 var millis = '00' + d.getMilliseconds();
        1224 millis = millis.substr(millis.length - 3);
        1225
        1226 return this.pad_(d.getHours()) + ':' + this.pad_(d.getMinutes()) + ':' +
        1227 this.pad_(d.getSeconds()) + '.' + millis;
        1228};
        1229
        1230
        1231/**
        1232 * Pads a number to make it have a leading zero if it's less than 10.
        1233 * @param {number} number The number to pad.
        1234 * @return {string} The resulting string.
        1235 * @private
        1236 */
        1237goog.testing.TestCase.prototype.pad_ = function(number) {
        1238 return number < 10 ? '0' + number : String(number);
        1239};
        1240
        1241
        1242/**
        1243 * Trims a path to be only that after google3.
        1244 * @param {string} path The path to trim.
        1245 * @return {string} The resulting string.
        1246 * @private
        1247 */
        1248goog.testing.TestCase.prototype.trimPath_ = function(path) {
        1249 return path.substring(path.indexOf('google3') + 8);
        1250};
        1251
        1252
        1253/**
        1254 * Handles a test that passed.
        1255 * @param {goog.testing.TestCase.Test} test The test that passed.
        1256 * @protected
        1257 */
        1258goog.testing.TestCase.prototype.doSuccess = function(test) {
        1259 this.result_.successCount++;
        1260 // An empty list of error messages indicates that the test passed.
        1261 // If we already have a failure for this test, do not set to empty list.
        1262 if (!(test.name in this.result_.resultsByName)) {
        1263 this.result_.resultsByName[test.name] = [];
        1264 }
        1265 var message = test.name + ' : PASSED';
        1266 this.saveMessage(message);
        1267 this.log(message);
        1268};
        1269
        1270
        1271/**
        1272 * Handles a test that failed.
        1273 * @param {goog.testing.TestCase.Test} test The test that failed.
        1274 * @param {*=} opt_e The exception object associated with the
        1275 * failure or a string.
        1276 * @protected
        1277 */
        1278goog.testing.TestCase.prototype.doError = function(test, opt_e) {
        1279 var message = test.name + ' : FAILED';
        1280 this.log(message);
        1281 this.saveMessage(message);
        1282 var err = this.logError(test.name, opt_e);
        1283 this.result_.errors.push(err);
        1284 if (test.name in this.result_.resultsByName) {
        1285 this.result_.resultsByName[test.name].push(err.toString());
        1286 } else {
        1287 this.result_.resultsByName[test.name] = [err.toString()];
        1288 }
        1289};
        1290
        1291
        1292/**
        1293 * @param {string} name Failed test name.
        1294 * @param {*=} opt_e The exception object associated with the
        1295 * failure or a string.
        1296 * @return {!goog.testing.TestCase.Error} Error object.
        1297 */
        1298goog.testing.TestCase.prototype.logError = function(name, opt_e) {
        1299 var errMsg = null;
        1300 var stack = null;
        1301 if (opt_e) {
        1302 this.log(opt_e);
        1303 if (goog.isString(opt_e)) {
        1304 errMsg = opt_e;
        1305 } else {
        1306 errMsg = opt_e.message || opt_e.description || opt_e.toString();
        1307 stack = opt_e.stack ? goog.testing.stacktrace.canonicalize(opt_e.stack) :
        1308 opt_e['stackTrace'];
        1309 }
        1310 } else {
        1311 errMsg = 'An unknown error occurred';
        1312 }
        1313 var err = new goog.testing.TestCase.Error(name, errMsg, stack);
        1314
        1315 // Avoid double logging.
        1316 if (!opt_e || !opt_e['isJsUnitException'] ||
        1317 !opt_e['loggedJsUnitException']) {
        1318 this.saveMessage(err.toString());
        1319 }
        1320 if (opt_e && opt_e['isJsUnitException']) {
        1321 opt_e['loggedJsUnitException'] = true;
        1322 }
        1323
        1324 return err;
        1325};
        1326
        1327
        1328
        1329/**
        1330 * A class representing a single test function.
        1331 * @param {string} name The test name.
        1332 * @param {Function} ref Reference to the test function.
        1333 * @param {Object=} opt_scope Optional scope that the test function should be
        1334 * called in.
        1335 * @constructor
        1336 */
        1337goog.testing.TestCase.Test = function(name, ref, opt_scope) {
        1338 /**
        1339 * The name of the test.
        1340 * @type {string}
        1341 */
        1342 this.name = name;
        1343
        1344 /**
        1345 * Reference to the test function.
        1346 * @type {Function}
        1347 */
        1348 this.ref = ref;
        1349
        1350 /**
        1351 * Scope that the test function should be called in.
        1352 * @type {Object}
        1353 */
        1354 this.scope = opt_scope || null;
        1355};
        1356
        1357
        1358/**
        1359 * Executes the test function.
        1360 * @package
        1361 */
        1362goog.testing.TestCase.Test.prototype.execute = function() {
        1363 this.ref.call(this.scope);
        1364};
        1365
        1366
        1367
        1368/**
        1369 * A class for representing test results. A bag of public properties.
        1370 * @param {goog.testing.TestCase} testCase The test case that owns this result.
        1371 * @constructor
        1372 * @final
        1373 */
        1374goog.testing.TestCase.Result = function(testCase) {
        1375 /**
        1376 * The test case that owns this result.
        1377 * @type {goog.testing.TestCase}
        1378 * @private
        1379 */
        1380 this.testCase_ = testCase;
        1381
        1382 /**
        1383 * Total number of tests that should have been run.
        1384 * @type {number}
        1385 */
        1386 this.totalCount = 0;
        1387
        1388 /**
        1389 * Total number of tests that were actually run.
        1390 * @type {number}
        1391 */
        1392 this.runCount = 0;
        1393
        1394 /**
        1395 * Number of successful tests.
        1396 * @type {number}
        1397 */
        1398 this.successCount = 0;
        1399
        1400 /**
        1401 * The amount of time the tests took to run.
        1402 * @type {number}
        1403 */
        1404 this.runTime = 0;
        1405
        1406 /**
        1407 * The number of files loaded to run this test.
        1408 * @type {number}
        1409 */
        1410 this.numFilesLoaded = 0;
        1411
        1412 /**
        1413 * Whether this test case was suppressed by shouldRunTests() returning false.
        1414 * @type {boolean}
        1415 */
        1416 this.testSuppressed = false;
        1417
        1418 /**
        1419 * Test results for each test that was run. The test name is always added
        1420 * as the key in the map, and the array of strings is an optional list
        1421 * of failure messages. If the array is empty, the test passed. Otherwise,
        1422 * the test failed.
        1423 * @type {!Object<string, !Array<string>>}
        1424 */
        1425 this.resultsByName = {};
        1426
        1427 /**
        1428 * Errors encountered while running the test.
        1429 * @type {!Array<goog.testing.TestCase.Error>}
        1430 */
        1431 this.errors = [];
        1432
        1433 /**
        1434 * Messages to show the user after running the test.
        1435 * @type {!Array<string>}
        1436 */
        1437 this.messages = [];
        1438
        1439 /**
        1440 * Whether the tests have completed.
        1441 * @type {boolean}
        1442 */
        1443 this.complete = false;
        1444};
        1445
        1446
        1447/**
        1448 * @return {boolean} Whether the test was successful.
        1449 */
        1450goog.testing.TestCase.Result.prototype.isSuccess = function() {
        1451 return this.complete && this.errors.length == 0;
        1452};
        1453
        1454
        1455/**
        1456 * @return {string} A summary of the tests, including total number of tests that
        1457 * passed, failed, and the time taken.
        1458 */
        1459goog.testing.TestCase.Result.prototype.getSummary = function() {
        1460 var summary = this.runCount + ' of ' + this.totalCount + ' tests run in ' +
        1461 this.runTime + 'ms.\n';
        1462 if (this.testSuppressed) {
        1463 summary += 'Tests not run because shouldRunTests() returned false.';
        1464 } else {
        1465 var failures = this.totalCount - this.successCount;
        1466 var suppressionMessage = '';
        1467
        1468 var countOfRunTests = this.testCase_.getActuallyRunCount();
        1469 if (countOfRunTests) {
        1470 failures = countOfRunTests - this.successCount;
        1471 suppressionMessage = ', ' +
        1472 (this.totalCount - countOfRunTests) + ' suppressed by querystring';
        1473 }
        1474 summary += this.successCount + ' passed, ' +
        1475 failures + ' failed' + suppressionMessage + '.\n' +
        1476 Math.round(this.runTime / this.runCount) + ' ms/test. ' +
        1477 this.numFilesLoaded + ' files loaded.';
        1478 }
        1479
        1480 return summary;
        1481};
        1482
        1483
        1484/**
        1485 * Initializes the given test case with the global test runner 'G_testRunner'.
        1486 * @param {goog.testing.TestCase} testCase The test case to install.
        1487 */
        1488goog.testing.TestCase.initializeTestRunner = function(testCase) {
        1489 testCase.autoDiscoverTests();
        1490
        1491 if (goog.global.location) {
        1492 var search = goog.global.location.search;
        1493 testCase.setOrder(goog.testing.TestCase.parseOrder_(search) ||
        1494 goog.testing.TestCase.Order.SORTED);
        1495 testCase.setTestsToRun(goog.testing.TestCase.parseRunTests_(search));
        1496 }
        1497
        1498 var gTestRunner = goog.global['G_testRunner'];
        1499 if (gTestRunner) {
        1500 gTestRunner['initialize'](testCase);
        1501 } else {
        1502 throw Error('G_testRunner is undefined. Please ensure goog.testing.jsunit' +
        1503 ' is included.');
        1504 }
        1505};
        1506
        1507
        1508/**
        1509 * Parses URL query parameters for the 'order' parameter.
        1510 * @param {string} search The URL query string.
        1511 * @return {?goog.testing.TestCase.Order} The sort order for running tests.
        1512 * @private
        1513 */
        1514goog.testing.TestCase.parseOrder_ = function(search) {
        1515 var order = null;
        1516 var orderMatch = search.match(
        1517 /(?:\?|&)order=(natural|random|sorted)/i);
        1518 if (orderMatch) {
        1519 order = /** @type {goog.testing.TestCase.Order} */ (
        1520 orderMatch[1].toLowerCase());
        1521 }
        1522 return order;
        1523};
        1524
        1525
        1526/**
        1527 * Parses URL query parameters for the 'runTests' parameter.
        1528 * @param {string} search The URL query string.
        1529 * @return {Object<string, boolean>} A set of test names or test indices to be
        1530 * run by the test runner.
        1531 * @private
        1532 */
        1533goog.testing.TestCase.parseRunTests_ = function(search) {
        1534 var testsToRun = null;
        1535 var runTestsMatch = search.match(/(?:\?|&)runTests=([^?&]+)/i);
        1536 if (runTestsMatch) {
        1537 testsToRun = {};
        1538 var arr = runTestsMatch[1].split(',');
        1539 for (var i = 0, len = arr.length; i < len; i++) {
        1540 testsToRun[arr[i]] = true;
        1541 }
        1542 }
        1543 return testsToRun;
        1544};
        1545
        1546
        1547/**
        1548 * Wraps provided promise and returns a new promise which will be rejected
        1549 * if the original promise does not settle within the given timeout.
        1550 * @param {!IThenable<T>} promise
        1551 * @param {number} timeoutInMs Number of milliseconds to wait for the promise to
        1552 * settle before failing it with a timeout error.
        1553 * @param {string} errorMsg Error message to use if the promise times out.
        1554 * @return {!goog.Promise<T>} A promise that will settle with the original
        1555 promise unless the timeout is exceeded.
        1556 * errror.
        1557 * @template T
        1558 * @private
        1559 */
        1560goog.testing.TestCase.prototype.rejectIfPromiseTimesOut_ =
        1561 function(promise, timeoutInMs, errorMsg) {
        1562 var self = this;
        1563 var start = this.now();
        1564 return new goog.Promise(function(resolve, reject) {
        1565 var timeoutId = self.timeout(function() {
        1566 var elapsed = self.now() - start;
        1567 reject(new Error(errorMsg + '\nElapsed time: ' + elapsed + 'ms.'));
        1568 }, timeoutInMs);
        1569 promise.then(resolve, reject);
        1570 var clearTimeout = goog.bind(self.clearTimeout, self, timeoutId);
        1571 promise.then(clearTimeout, clearTimeout);
        1572 });
        1573};
        1574
        1575
        1576
        1577/**
        1578 * A class representing an error thrown by the test
        1579 * @param {string} source The name of the test which threw the error.
        1580 * @param {string} message The error message.
        1581 * @param {string=} opt_stack A string showing the execution stack.
        1582 * @constructor
        1583 * @final
        1584 */
        1585goog.testing.TestCase.Error = function(source, message, opt_stack) {
        1586 /**
        1587 * The name of the test which threw the error.
        1588 * @type {string}
        1589 */
        1590 this.source = source;
        1591
        1592 /**
        1593 * Reference to the test function.
        1594 * @type {string}
        1595 */
        1596 this.message = message;
        1597
        1598 /**
        1599 * The stack.
        1600 * @type {?string}
        1601 */
        1602 this.stack = null;
        1603
        1604 if (opt_stack) {
        1605 this.stack = opt_stack;
        1606 } else {
        1607 // Attempt to capture a stack trace.
        1608 if (Error.captureStackTrace) {
        1609 // See https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi
        1610 Error.captureStackTrace(this, goog.testing.TestCase.Error);
        1611 } else {
        1612 var stack = new Error().stack;
        1613 if (stack) {
        1614 this.stack = stack;
        1615 }
        1616 }
        1617 }
        1618};
        1619
        1620
        1621/**
        1622 * Returns a string representing the error object.
        1623 * @return {string} A string representation of the error.
        1624 * @override
        1625 */
        1626goog.testing.TestCase.Error.prototype.toString = function() {
        1627 return 'ERROR in ' + this.source + '\n' +
        1628 this.message + (this.stack ? '\n' + this.stack : '');
        1629};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/testrunner.js.src.html b/docs/source/lib/goog/testing/testrunner.js.src.html new file mode 100644 index 0000000..8edf546 --- /dev/null +++ b/docs/source/lib/goog/testing/testrunner.js.src.html @@ -0,0 +1 @@ +testrunner.js

        lib/goog/testing/testrunner.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 The test runner is a singleton object that is used to execute
        17 * a goog.testing.TestCases, display the results, and expose the results to
        18 * Selenium for automation. If a TestCase hasn't been registered with the
        19 * runner by the time window.onload occurs, the testRunner will try to auto-
        20 * discover JsUnit style test pages.
        21 *
        22 * The hooks for selenium are (see http://go/selenium-hook-setup):-
        23 * - Boolean G_testRunner.isFinished()
        24 * - Boolean G_testRunner.isSuccess()
        25 * - String G_testRunner.getReport()
        26 * - number G_testRunner.getRunTime()
        27 * - Object<string, Array<string>> G_testRunner.getTestResults()
        28 *
        29 * Testing code should not have dependencies outside of goog.testing so as to
        30 * reduce the chance of masking missing dependencies.
        31 *
        32 */
        33
        34goog.provide('goog.testing.TestRunner');
        35
        36goog.require('goog.dom.TagName');
        37goog.require('goog.testing.TestCase');
        38
        39
        40
        41/**
        42 * Construct a test runner.
        43 *
        44 * NOTE(user): This is currently pretty weird, I'm essentially trying to
        45 * create a wrapper that the Selenium test can hook into to query the state of
        46 * the running test case, while making goog.testing.TestCase general.
        47 *
        48 * @constructor
        49 */
        50goog.testing.TestRunner = function() {
        51 /**
        52 * Errors that occurred in the window.
        53 * @type {Array<string>}
        54 */
        55 this.errors = [];
        56};
        57
        58
        59/**
        60 * Reference to the active test case.
        61 * @type {goog.testing.TestCase?}
        62 */
        63goog.testing.TestRunner.prototype.testCase = null;
        64
        65
        66/**
        67 * Whether the test runner has been initialized yet.
        68 * @type {boolean}
        69 */
        70goog.testing.TestRunner.prototype.initialized = false;
        71
        72
        73/**
        74 * Element created in the document to add test results to.
        75 * @type {Element}
        76 * @private
        77 */
        78goog.testing.TestRunner.prototype.logEl_ = null;
        79
        80
        81/**
        82 * Function to use when filtering errors.
        83 * @type {(function(string))?}
        84 * @private
        85 */
        86goog.testing.TestRunner.prototype.errorFilter_ = null;
        87
        88
        89/**
        90 * Whether an empty test case counts as an error.
        91 * @type {boolean}
        92 * @private
        93 */
        94goog.testing.TestRunner.prototype.strict_ = true;
        95
        96
        97/**
        98 * Initializes the test runner.
        99 * @param {goog.testing.TestCase} testCase The test case to initialize with.
        100 */
        101goog.testing.TestRunner.prototype.initialize = function(testCase) {
        102 if (this.testCase && this.testCase.running) {
        103 throw Error('The test runner is already waiting for a test to complete');
        104 }
        105 this.testCase = testCase;
        106 this.initialized = true;
        107};
        108
        109
        110/**
        111 * By default, the test runner is strict, and fails if it runs an empty
        112 * test case.
        113 * @param {boolean} strict Whether the test runner should fail on an empty
        114 * test case.
        115 */
        116goog.testing.TestRunner.prototype.setStrict = function(strict) {
        117 this.strict_ = strict;
        118};
        119
        120
        121/**
        122 * @return {boolean} Whether the test runner should fail on an empty
        123 * test case.
        124 */
        125goog.testing.TestRunner.prototype.isStrict = function() {
        126 return this.strict_;
        127};
        128
        129
        130/**
        131 * Returns true if the test runner is initialized.
        132 * Used by Selenium Hooks.
        133 * @return {boolean} Whether the test runner is active.
        134 */
        135goog.testing.TestRunner.prototype.isInitialized = function() {
        136 return this.initialized;
        137};
        138
        139
        140/**
        141 * Returns true if the test runner is finished.
        142 * Used by Selenium Hooks.
        143 * @return {boolean} Whether the test runner is active.
        144 */
        145goog.testing.TestRunner.prototype.isFinished = function() {
        146 return this.errors.length > 0 ||
        147 this.initialized && !!this.testCase && this.testCase.started &&
        148 !this.testCase.running;
        149};
        150
        151
        152/**
        153 * Returns true if the test case didn't fail.
        154 * Used by Selenium Hooks.
        155 * @return {boolean} Whether the current test returned successfully.
        156 */
        157goog.testing.TestRunner.prototype.isSuccess = function() {
        158 return !this.hasErrors() && !!this.testCase && this.testCase.isSuccess();
        159};
        160
        161
        162/**
        163 * Returns true if the test case runner has errors that were caught outside of
        164 * the test case.
        165 * @return {boolean} Whether there were JS errors.
        166 */
        167goog.testing.TestRunner.prototype.hasErrors = function() {
        168 return this.errors.length > 0;
        169};
        170
        171
        172/**
        173 * Logs an error that occurred. Used in the case of environment setting up
        174 * an onerror handler.
        175 * @param {string} msg Error message.
        176 */
        177goog.testing.TestRunner.prototype.logError = function(msg) {
        178 if (!this.errorFilter_ || this.errorFilter_.call(null, msg)) {
        179 this.errors.push(msg);
        180 }
        181};
        182
        183
        184/**
        185 * Log failure in current running test.
        186 * @param {Error} ex Exception.
        187 */
        188goog.testing.TestRunner.prototype.logTestFailure = function(ex) {
        189 var testName = /** @type {string} */ (goog.testing.TestCase.currentTestName);
        190 if (this.testCase) {
        191 this.testCase.logError(testName, ex);
        192 } else {
        193 // NOTE: Do not forget to log the original exception raised.
        194 throw new Error('Test runner not initialized with a test case. Original ' +
        195 'exception: ' + ex.message);
        196 }
        197};
        198
        199
        200/**
        201 * Sets a function to use as a filter for errors.
        202 * @param {function(string)} fn Filter function.
        203 */
        204goog.testing.TestRunner.prototype.setErrorFilter = function(fn) {
        205 this.errorFilter_ = fn;
        206};
        207
        208
        209/**
        210 * Returns a report of the test case that ran.
        211 * Used by Selenium Hooks.
        212 * @param {boolean=} opt_verbose If true results will include data about all
        213 * tests, not just what failed.
        214 * @return {string} A report summary of the test.
        215 */
        216goog.testing.TestRunner.prototype.getReport = function(opt_verbose) {
        217 var report = [];
        218 if (this.testCase) {
        219 report.push(this.testCase.getReport(opt_verbose));
        220 }
        221 if (this.errors.length > 0) {
        222 report.push('JavaScript errors detected by test runner:');
        223 report.push.apply(report, this.errors);
        224 report.push('\n');
        225 }
        226 return report.join('\n');
        227};
        228
        229
        230/**
        231 * Returns the amount of time it took for the test to run.
        232 * Used by Selenium Hooks.
        233 * @return {number} The run time, in milliseconds.
        234 */
        235goog.testing.TestRunner.prototype.getRunTime = function() {
        236 return this.testCase ? this.testCase.getRunTime() : 0;
        237};
        238
        239
        240/**
        241 * Returns the number of script files that were loaded in order to run the test.
        242 * @return {number} The number of script files.
        243 */
        244goog.testing.TestRunner.prototype.getNumFilesLoaded = function() {
        245 return this.testCase ? this.testCase.getNumFilesLoaded() : 0;
        246};
        247
        248
        249/**
        250 * Executes a test case and prints the results to the window.
        251 */
        252goog.testing.TestRunner.prototype.execute = function() {
        253 if (!this.testCase) {
        254 throw Error('The test runner must be initialized with a test case ' +
        255 'before execute can be called.');
        256 }
        257
        258 if (this.strict_ && this.testCase.getCount() == 0) {
        259 throw Error(
        260 'No tests found in given test case: ' +
        261 this.testCase.getName() + ' ' +
        262 'By default, the test runner fails if a test case has no tests. ' +
        263 'To modify this behavior, see goog.testing.TestRunner\'s ' +
        264 'setStrict() method, or G_testRunner.setStrict()');
        265 }
        266
        267 this.testCase.setCompletedCallback(goog.bind(this.onComplete_, this));
        268 if (goog.testing.TestRunner.shouldUsePromises_(this.testCase)) {
        269 this.testCase.runTestsReturningPromise();
        270 } else {
        271 this.testCase.runTests();
        272 }
        273};
        274
        275
        276/**
        277 * @param {!goog.testing.TestCase} testCase
        278 * @return {boolean}
        279 * @private
        280 */
        281goog.testing.TestRunner.shouldUsePromises_ = function(testCase) {
        282 return testCase.constructor === goog.testing.TestCase;
        283};
        284
        285
        286/**
        287 * Writes the results to the document when the test case completes.
        288 * @private
        289 */
        290goog.testing.TestRunner.prototype.onComplete_ = function() {
        291 var log = this.testCase.getReport(true);
        292 if (this.errors.length > 0) {
        293 log += '\n' + this.errors.join('\n');
        294 }
        295
        296 if (!this.logEl_) {
        297 var el = document.getElementById('closureTestRunnerLog');
        298 if (el == null) {
        299 el = document.createElement(goog.dom.TagName.DIV);
        300 document.body.appendChild(el);
        301 }
        302 this.logEl_ = el;
        303 }
        304
        305 // Highlight the page to indicate the overall outcome.
        306 this.writeLog(log);
        307
        308 // TODO(chrishenry): Make this work with multiple test cases (b/8603638).
        309 var runAgainLink = document.createElement(goog.dom.TagName.A);
        310 runAgainLink.style.display = 'inline-block';
        311 runAgainLink.style.fontSize = 'small';
        312 runAgainLink.style.marginBottom = '16px';
        313 runAgainLink.href = '';
        314 runAgainLink.onclick = goog.bind(function() {
        315 this.execute();
        316 return false;
        317 }, this);
        318 runAgainLink.innerHTML = 'Run again without reloading';
        319 this.logEl_.appendChild(runAgainLink);
        320};
        321
        322
        323/**
        324 * Writes a nicely formatted log out to the document.
        325 * @param {string} log The string to write.
        326 */
        327goog.testing.TestRunner.prototype.writeLog = function(log) {
        328 var lines = log.split('\n');
        329 for (var i = 0; i < lines.length; i++) {
        330 var line = lines[i];
        331 var color;
        332 var isFailOrError = /FAILED/.test(line) || /ERROR/.test(line);
        333 if (/PASSED/.test(line)) {
        334 color = 'darkgreen';
        335 } else if (isFailOrError) {
        336 color = 'darkred';
        337 } else {
        338 color = '#333';
        339 }
        340 var div = document.createElement(goog.dom.TagName.DIV);
        341 if (line.substr(0, 2) == '> ') {
        342 // The stack trace may contain links so it has to be interpreted as HTML.
        343 div.innerHTML = line;
        344 } else {
        345 div.appendChild(document.createTextNode(line));
        346 }
        347
        348 var testNameMatch =
        349 /(\S+) (\[[^\]]*] )?: (FAILED|ERROR|PASSED)/.exec(line);
        350 if (testNameMatch) {
        351 // Build a URL to run the test individually. If this test was already
        352 // part of another subset test, we need to overwrite the old runTests
        353 // query parameter. We also need to do this without bringing in any
        354 // extra dependencies, otherwise we could mask missing dependency bugs.
        355 var newSearch = 'runTests=' + testNameMatch[1];
        356 var search = window.location.search;
        357 if (search) {
        358 var oldTests = /runTests=([^&]*)/.exec(search);
        359 if (oldTests) {
        360 newSearch = search.substr(0, oldTests.index) +
        361 newSearch +
        362 search.substr(oldTests.index + oldTests[0].length);
        363 } else {
        364 newSearch = search + '&' + newSearch;
        365 }
        366 } else {
        367 newSearch = '?' + newSearch;
        368 }
        369 var href = window.location.href;
        370 var hash = window.location.hash;
        371 if (hash && hash.charAt(0) != '#') {
        372 hash = '#' + hash;
        373 }
        374 href = href.split('#')[0].split('?')[0] + newSearch + hash;
        375
        376 // Add the link.
        377 var a = document.createElement(goog.dom.TagName.A);
        378 a.innerHTML = '(run individually)';
        379 a.style.fontSize = '0.8em';
        380 a.style.color = '#888';
        381 a.href = href;
        382 div.appendChild(document.createTextNode(' '));
        383 div.appendChild(a);
        384 }
        385
        386 div.style.color = color;
        387 div.style.font = 'normal 100% monospace';
        388 div.style.wordWrap = 'break-word';
        389 if (i == 0) {
        390 // Highlight the first line as a header that indicates the test outcome.
        391 div.style.padding = '20px';
        392 div.style.marginBottom = '10px';
        393 if (isFailOrError) {
        394 div.style.border = '5px solid ' + color;
        395 div.style.backgroundColor = '#ffeeee';
        396 } else {
        397 div.style.border = '1px solid black';
        398 div.style.backgroundColor = '#eeffee';
        399 }
        400 }
        401
        402 try {
        403 div.style.whiteSpace = 'pre-wrap';
        404 } catch (e) {
        405 // NOTE(brenneman): IE raises an exception when assigning to pre-wrap.
        406 // Thankfully, it doesn't collapse whitespace when using monospace fonts,
        407 // so it will display correctly if we ignore the exception.
        408 }
        409
        410 if (i < 2) {
        411 div.style.fontWeight = 'bold';
        412 }
        413 this.logEl_.appendChild(div);
        414 }
        415};
        416
        417
        418/**
        419 * Logs a message to the current test case.
        420 * @param {string} s The text to output to the log.
        421 */
        422goog.testing.TestRunner.prototype.log = function(s) {
        423 if (this.testCase) {
        424 this.testCase.log(s);
        425 }
        426};
        427
        428
        429// TODO(nnaze): Properly handle serving test results when multiple test cases
        430// are run.
        431/**
        432 * @return {Object<string, !Array<string>>} A map of test names to a list of
        433 * test failures (if any) to provide formatted data for the test runner.
        434 */
        435goog.testing.TestRunner.prototype.getTestResults = function() {
        436 if (this.testCase) {
        437 return this.testCase.getTestResults();
        438 }
        439 return null;
        440};
        \ No newline at end of file diff --git a/docs/source/lib/goog/testing/watchers.js.src.html b/docs/source/lib/goog/testing/watchers.js.src.html new file mode 100644 index 0000000..af097a8 --- /dev/null +++ b/docs/source/lib/goog/testing/watchers.js.src.html @@ -0,0 +1 @@ +watchers.js

        lib/goog/testing/watchers.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 Simple notifiers for the Closure testing framework.
        17 *
        18 * @author johnlenz@google.com (John Lenz)
        19 */
        20
        21goog.provide('goog.testing.watchers');
        22
        23
        24/** @private {!Array.<function()>} */
        25goog.testing.watchers.resetWatchers_ = [];
        26
        27
        28/**
        29 * Fires clock reset watching functions.
        30 */
        31goog.testing.watchers.signalClockReset = function() {
        32 var watchers = goog.testing.watchers.resetWatchers_;
        33 for (var i = 0; i < watchers.length; i++) {
        34 goog.testing.watchers.resetWatchers_[i]();
        35 }
        36};
        37
        38
        39/**
        40 * Enqueues a function to be called when the clock used for setTimeout is reset.
        41 * @param {function()} fn
        42 */
        43goog.testing.watchers.watchClockReset = function(fn) {
        44 goog.testing.watchers.resetWatchers_.push(fn);
        45};
        46
        \ No newline at end of file diff --git a/docs/source/lib/goog/uri/uri.js.src.html b/docs/source/lib/goog/uri/uri.js.src.html index 99b4e2b..8bd4094 100644 --- a/docs/source/lib/goog/uri/uri.js.src.html +++ b/docs/source/lib/goog/uri/uri.js.src.html @@ -1 +1 @@ -uri.js

        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
        32goog.provide('goog.Uri');
        33goog.provide('goog.Uri.QueryData');
        34
        35goog.require('goog.array');
        36goog.require('goog.string');
        37goog.require('goog.structs');
        38goog.require('goog.structs.Map');
        39goog.require('goog.uri.utils');
        40goog.require('goog.uri.utils.ComponentIndex');
        41goog.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 */
        66goog.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 */
        113goog.Uri.preserveParameterTypesCompatibilityFlag = false;
        114
        115
        116/**
        117 * Parameter name added to stop caching.
        118 * @type {string}
        119 */
        120goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;
        121
        122
        123/**
        124 * Scheme such as "http".
        125 * @type {string}
        126 * @private
        127 */
        128goog.Uri.prototype.scheme_ = '';
        129
        130
        131/**
        132 * User credentials in the form "username:password".
        133 * @type {string}
        134 * @private
        135 */
        136goog.Uri.prototype.userInfo_ = '';
        137
        138
        139/**
        140 * Domain part, e.g. "www.google.com".
        141 * @type {string}
        142 * @private
        143 */
        144goog.Uri.prototype.domain_ = '';
        145
        146
        147/**
        148 * Port, e.g. 8080.
        149 * @type {?number}
        150 * @private
        151 */
        152goog.Uri.prototype.port_ = null;
        153
        154
        155/**
        156 * Path, e.g. "/tests/img.png".
        157 * @type {string}
        158 * @private
        159 */
        160goog.Uri.prototype.path_ = '';
        161
        162
        163/**
        164 * Object representing query data.
        165 * @type {!goog.Uri.QueryData}
        166 * @private
        167 */
        168goog.Uri.prototype.queryData_;
        169
        170
        171/**
        172 * The fragment without the #.
        173 * @type {string}
        174 * @private
        175 */
        176goog.Uri.prototype.fragment_ = '';
        177
        178
        179/**
        180 * Whether or not this Uri should be treated as Read Only.
        181 * @type {boolean}
        182 * @private
        183 */
        184goog.Uri.prototype.isReadOnly_ = false;
        185
        186
        187/**
        188 * Whether or not to ignore case when comparing query params.
        189 * @type {boolean}
        190 * @private
        191 */
        192goog.Uri.prototype.ignoreCase_ = false;
        193
        194
        195/**
        196 * @return {string} The string form of the url.
        197 * @override
        198 */
        199goog.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 */
        269goog.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 objcet.
        343 */
        344goog.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 */
        352goog.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 */
        363goog.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 */
        379goog.Uri.prototype.hasScheme = function() {
        380 return !!this.scheme_;
        381};
        382
        383
        384/**
        385 * @return {string} The decoded user info.
        386 */
        387goog.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 */
        398goog.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 */
        409goog.Uri.prototype.hasUserInfo = function() {
        410 return !!this.userInfo_;
        411};
        412
        413
        414/**
        415 * @return {string} The decoded domain.
        416 */
        417goog.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 */
        428goog.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 */
        438goog.Uri.prototype.hasDomain = function() {
        439 return !!this.domain_;
        440};
        441
        442
        443/**
        444 * @return {?number} The port number.
        445 */
        446goog.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 */
        456goog.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 */
        476goog.Uri.prototype.hasPort = function() {
        477 return this.port_ != null;
        478};
        479
        480
        481/**
        482 * @return {string} The decoded path.
        483 */
        484goog.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 */
        495goog.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 */
        505goog.Uri.prototype.hasPath = function() {
        506 return !!this.path_;
        507};
        508
        509
        510/**
        511 * @return {boolean} Whether the query string has been set.
        512 */
        513goog.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 */
        525goog.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 */
        551goog.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 */
        559goog.Uri.prototype.getEncodedQuery = function() {
        560 return this.queryData_.toString();
        561};
        562
        563
        564/**
        565 * @return {string} The decoded URI query, not including the ?.
        566 */
        567goog.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 */
        576goog.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 */
        587goog.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 */
        600goog.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 */
        620goog.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 */
        641goog.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 */
        654goog.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 */
        665goog.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 */
        676goog.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 */
        687goog.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 */
        697goog.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 */
        709goog.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 */
        723goog.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 */
        737goog.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 */
        746goog.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 */
        755goog.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 */
        769goog.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 */
        781goog.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 */
        801goog.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 */
        822goog.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 */
        848goog.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 */
        868goog.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 */
        914goog.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 */
        930goog.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 */
        944goog.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 */
        956goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g;
        957
        958
        959/**
        960 * Regular expression for characters that are disallowed in a relative path.
        961 * @type {RegExp}
        962 * @private
        963 */
        964goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g;
        965
        966
        967/**
        968 * Regular expression for characters that are disallowed in an absolute path.
        969 * @type {RegExp}
        970 * @private
        971 */
        972goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g;
        973
        974
        975/**
        976 * Regular expression for characters that are disallowed in the query.
        977 * @type {RegExp}
        978 * @private
        979 */
        980goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g;
        981
        982
        983/**
        984 * Regular expression for characters that are disallowed in the fragment.
        985 * @type {RegExp}
        986 * @private
        987 */
        988goog.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 */
        997goog.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 */
        1025goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) {
        1026 /**
        1027 * Encoded query string, or null if it requires computing from the key map.
        1028 * @type {?string}
        1029 * @private
        1030 */
        1031 this.encodedQuery_ = opt_query || null;
        1032
        1033 /**
        1034 * If true, ignore the case of the parameter name in #get.
        1035 * @type {boolean}
        1036 * @private
        1037 */
        1038 this.ignoreCase_ = !!opt_ignoreCase;
        1039};
        1040
        1041
        1042/**
        1043 * If the underlying key map is not yet initialized, it parses the
        1044 * query string and fills the map with parsed data.
        1045 * @private
        1046 */
        1047goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {
        1048 if (!this.keyMap_) {
        1049 this.keyMap_ = new goog.structs.Map();
        1050 this.count_ = 0;
        1051
        1052 if (this.encodedQuery_) {
        1053 var pairs = this.encodedQuery_.split('&');
        1054 for (var i = 0; i < pairs.length; i++) {
        1055 var indexOfEquals = pairs[i].indexOf('=');
        1056 var name = null;
        1057 var value = null;
        1058 if (indexOfEquals >= 0) {
        1059 name = pairs[i].substring(0, indexOfEquals);
        1060 value = pairs[i].substring(indexOfEquals + 1);
        1061 } else {
        1062 name = pairs[i];
        1063 }
        1064 name = goog.string.urlDecode(name);
        1065 name = this.getKeyName_(name);
        1066 this.add(name, value ? goog.string.urlDecode(value) : '');
        1067 }
        1068 }
        1069 }
        1070};
        1071
        1072
        1073/**
        1074 * Creates a new query data instance from a map of names and values.
        1075 *
        1076 * @param {!goog.structs.Map|!Object} map Map of string parameter
        1077 * names to parameter value. If parameter value is an array, it is
        1078 * treated as if the key maps to each individual value in the
        1079 * array.
        1080 * @param {goog.Uri=} opt_uri URI object that should have its cache
        1081 * invalidated when this object updates.
        1082 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
        1083 * name in #get.
        1084 * @return {!goog.Uri.QueryData} The populated query data instance.
        1085 */
        1086goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) {
        1087 var keys = goog.structs.getKeys(map);
        1088 if (typeof keys == 'undefined') {
        1089 throw Error('Keys are undefined');
        1090 }
        1091
        1092 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
        1093 var values = goog.structs.getValues(map);
        1094 for (var i = 0; i < keys.length; i++) {
        1095 var key = keys[i];
        1096 var value = values[i];
        1097 if (!goog.isArray(value)) {
        1098 queryData.add(key, value);
        1099 } else {
        1100 queryData.setValues(key, value);
        1101 }
        1102 }
        1103 return queryData;
        1104};
        1105
        1106
        1107/**
        1108 * Creates a new query data instance from parallel arrays of parameter names
        1109 * and values. Allows for duplicate parameter names. Throws an error if the
        1110 * lengths of the arrays differ.
        1111 *
        1112 * @param {Array.<string>} keys Parameter names.
        1113 * @param {Array} values Parameter values.
        1114 * @param {goog.Uri=} opt_uri URI object that should have its cache
        1115 * invalidated when this object updates.
        1116 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
        1117 * name in #get.
        1118 * @return {!goog.Uri.QueryData} The populated query data instance.
        1119 */
        1120goog.Uri.QueryData.createFromKeysValues = function(
        1121 keys, values, opt_uri, opt_ignoreCase) {
        1122 if (keys.length != values.length) {
        1123 throw Error('Mismatched lengths for keys/values');
        1124 }
        1125 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
        1126 for (var i = 0; i < keys.length; i++) {
        1127 queryData.add(keys[i], values[i]);
        1128 }
        1129 return queryData;
        1130};
        1131
        1132
        1133/**
        1134 * The map containing name/value or name/array-of-values pairs.
        1135 * May be null if it requires parsing from the query string.
        1136 *
        1137 * We need to use a Map because we cannot guarantee that the key names will
        1138 * not be problematic for IE.
        1139 *
        1140 * @type {goog.structs.Map}
        1141 * @private
        1142 */
        1143goog.Uri.QueryData.prototype.keyMap_ = null;
        1144
        1145
        1146/**
        1147 * The number of params, or null if it requires computing.
        1148 * @type {?number}
        1149 * @private
        1150 */
        1151goog.Uri.QueryData.prototype.count_ = null;
        1152
        1153
        1154/**
        1155 * @return {?number} The number of parameters.
        1156 */
        1157goog.Uri.QueryData.prototype.getCount = function() {
        1158 this.ensureKeyMapInitialized_();
        1159 return this.count_;
        1160};
        1161
        1162
        1163/**
        1164 * Adds a key value pair.
        1165 * @param {string} key Name.
        1166 * @param {*} value Value.
        1167 * @return {!goog.Uri.QueryData} Instance of this object.
        1168 */
        1169goog.Uri.QueryData.prototype.add = function(key, value) {
        1170 this.ensureKeyMapInitialized_();
        1171 this.invalidateCache_();
        1172
        1173 key = this.getKeyName_(key);
        1174 var values = this.keyMap_.get(key);
        1175 if (!values) {
        1176 this.keyMap_.set(key, (values = []));
        1177 }
        1178 values.push(value);
        1179 this.count_++;
        1180 return this;
        1181};
        1182
        1183
        1184/**
        1185 * Removes all the params with the given key.
        1186 * @param {string} key Name.
        1187 * @return {boolean} Whether any parameter was removed.
        1188 */
        1189goog.Uri.QueryData.prototype.remove = function(key) {
        1190 this.ensureKeyMapInitialized_();
        1191
        1192 key = this.getKeyName_(key);
        1193 if (this.keyMap_.containsKey(key)) {
        1194 this.invalidateCache_();
        1195
        1196 // Decrement parameter count.
        1197 this.count_ -= this.keyMap_.get(key).length;
        1198 return this.keyMap_.remove(key);
        1199 }
        1200 return false;
        1201};
        1202
        1203
        1204/**
        1205 * Clears the parameters.
        1206 */
        1207goog.Uri.QueryData.prototype.clear = function() {
        1208 this.invalidateCache_();
        1209 this.keyMap_ = null;
        1210 this.count_ = 0;
        1211};
        1212
        1213
        1214/**
        1215 * @return {boolean} Whether we have any parameters.
        1216 */
        1217goog.Uri.QueryData.prototype.isEmpty = function() {
        1218 this.ensureKeyMapInitialized_();
        1219 return this.count_ == 0;
        1220};
        1221
        1222
        1223/**
        1224 * Whether there is a parameter with the given name
        1225 * @param {string} key The parameter name to check for.
        1226 * @return {boolean} Whether there is a parameter with the given name.
        1227 */
        1228goog.Uri.QueryData.prototype.containsKey = function(key) {
        1229 this.ensureKeyMapInitialized_();
        1230 key = this.getKeyName_(key);
        1231 return this.keyMap_.containsKey(key);
        1232};
        1233
        1234
        1235/**
        1236 * Whether there is a parameter with the given value.
        1237 * @param {*} value The value to check for.
        1238 * @return {boolean} Whether there is a parameter with the given value.
        1239 */
        1240goog.Uri.QueryData.prototype.containsValue = function(value) {
        1241 // NOTE(arv): This solution goes through all the params even if it was the
        1242 // first param. We can get around this by not reusing code or by switching to
        1243 // iterators.
        1244 var vals = this.getValues();
        1245 return goog.array.contains(vals, value);
        1246};
        1247
        1248
        1249/**
        1250 * Returns all the keys of the parameters. If a key is used multiple times
        1251 * it will be included multiple times in the returned array
        1252 * @return {!Array.<string>} All the keys of the parameters.
        1253 */
        1254goog.Uri.QueryData.prototype.getKeys = function() {
        1255 this.ensureKeyMapInitialized_();
        1256 // We need to get the values to know how many keys to add.
        1257 var vals = /** @type {Array.<Array|*>} */ (this.keyMap_.getValues());
        1258 var keys = this.keyMap_.getKeys();
        1259 var rv = [];
        1260 for (var i = 0; i < keys.length; i++) {
        1261 var val = vals[i];
        1262 for (var j = 0; j < val.length; j++) {
        1263 rv.push(keys[i]);
        1264 }
        1265 }
        1266 return rv;
        1267};
        1268
        1269
        1270/**
        1271 * Returns all the values of the parameters with the given name. If the query
        1272 * data has no such key this will return an empty array. If no key is given
        1273 * all values wil be returned.
        1274 * @param {string=} opt_key The name of the parameter to get the values for.
        1275 * @return {!Array} All the values of the parameters with the given name.
        1276 */
        1277goog.Uri.QueryData.prototype.getValues = function(opt_key) {
        1278 this.ensureKeyMapInitialized_();
        1279 var rv = [];
        1280 if (opt_key) {
        1281 if (this.containsKey(opt_key)) {
        1282 rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));
        1283 }
        1284 } else {
        1285 // Return all values.
        1286 var values = /** @type {Array.<Array|*>} */ (this.keyMap_.getValues());
        1287 for (var i = 0; i < values.length; i++) {
        1288 rv = goog.array.concat(rv, values[i]);
        1289 }
        1290 }
        1291 return rv;
        1292};
        1293
        1294
        1295/**
        1296 * Sets a key value pair and removes all other keys with the same value.
        1297 *
        1298 * @param {string} key Name.
        1299 * @param {*} value Value.
        1300 * @return {!goog.Uri.QueryData} Instance of this object.
        1301 */
        1302goog.Uri.QueryData.prototype.set = function(key, value) {
        1303 this.ensureKeyMapInitialized_();
        1304 this.invalidateCache_();
        1305
        1306 // TODO(user): This could be better written as
        1307 // this.remove(key), this.add(key, value), but that would reorder
        1308 // the key (since the key is first removed and then added at the
        1309 // end) and we would have to fix unit tests that depend on key
        1310 // ordering.
        1311 key = this.getKeyName_(key);
        1312 if (this.containsKey(key)) {
        1313 this.count_ -= this.keyMap_.get(key).length;
        1314 }
        1315 this.keyMap_.set(key, [value]);
        1316 this.count_++;
        1317 return this;
        1318};
        1319
        1320
        1321/**
        1322 * Returns the first value associated with the key. If the query data has no
        1323 * such key this will return undefined or the optional default.
        1324 * @param {string} key The name of the parameter to get the value for.
        1325 * @param {*=} opt_default The default value to return if the query data
        1326 * has no such key.
        1327 * @return {*} The first string value associated with the key, or opt_default
        1328 * if there's no value.
        1329 */
        1330goog.Uri.QueryData.prototype.get = function(key, opt_default) {
        1331 var values = key ? this.getValues(key) : [];
        1332 if (goog.Uri.preserveParameterTypesCompatibilityFlag) {
        1333 return values.length > 0 ? values[0] : opt_default;
        1334 } else {
        1335 return values.length > 0 ? String(values[0]) : opt_default;
        1336 }
        1337};
        1338
        1339
        1340/**
        1341 * Sets the values for a key. If the key already exists, this will
        1342 * override all of the existing values that correspond to the key.
        1343 * @param {string} key The key to set values for.
        1344 * @param {Array} values The values to set.
        1345 */
        1346goog.Uri.QueryData.prototype.setValues = function(key, values) {
        1347 this.remove(key);
        1348
        1349 if (values.length > 0) {
        1350 this.invalidateCache_();
        1351 this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));
        1352 this.count_ += values.length;
        1353 }
        1354};
        1355
        1356
        1357/**
        1358 * @return {string} Encoded query string.
        1359 * @override
        1360 */
        1361goog.Uri.QueryData.prototype.toString = function() {
        1362 if (this.encodedQuery_) {
        1363 return this.encodedQuery_;
        1364 }
        1365
        1366 if (!this.keyMap_) {
        1367 return '';
        1368 }
        1369
        1370 var sb = [];
        1371
        1372 // In the past, we use this.getKeys() and this.getVals(), but that
        1373 // generates a lot of allocations as compared to simply iterating
        1374 // over the keys.
        1375 var keys = this.keyMap_.getKeys();
        1376 for (var i = 0; i < keys.length; i++) {
        1377 var key = keys[i];
        1378 var encodedKey = goog.string.urlEncode(key);
        1379 var val = this.getValues(key);
        1380 for (var j = 0; j < val.length; j++) {
        1381 var param = encodedKey;
        1382 // Ensure that null and undefined are encoded into the url as
        1383 // literal strings.
        1384 if (val[j] !== '') {
        1385 param += '=' + goog.string.urlEncode(val[j]);
        1386 }
        1387 sb.push(param);
        1388 }
        1389 }
        1390
        1391 return this.encodedQuery_ = sb.join('&');
        1392};
        1393
        1394
        1395/**
        1396 * @return {string} Decoded query string.
        1397 */
        1398goog.Uri.QueryData.prototype.toDecodedString = function() {
        1399 return goog.Uri.decodeOrEmpty_(this.toString());
        1400};
        1401
        1402
        1403/**
        1404 * Invalidate the cache.
        1405 * @private
        1406 */
        1407goog.Uri.QueryData.prototype.invalidateCache_ = function() {
        1408 this.encodedQuery_ = null;
        1409};
        1410
        1411
        1412/**
        1413 * Removes all keys that are not in the provided list. (Modifies this object.)
        1414 * @param {Array.<string>} keys The desired keys.
        1415 * @return {!goog.Uri.QueryData} a reference to this object.
        1416 */
        1417goog.Uri.QueryData.prototype.filterKeys = function(keys) {
        1418 this.ensureKeyMapInitialized_();
        1419 goog.structs.forEach(this.keyMap_,
        1420 /** @this {goog.Uri.QueryData} */
        1421 function(value, key, map) {
        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 */
        1434goog.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 */
        1452goog.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 */
        1467goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {
        1468 var resetKeys = ignoreCase && !this.ignoreCase_;
        1469 if (resetKeys) {
        1470 this.ensureKeyMapInitialized_();
        1471 this.invalidateCache_();
        1472 goog.structs.forEach(this.keyMap_,
        1473 /** @this {goog.Uri.QueryData} */
        1474 function(value, key) {
        1475 var lowerCase = key.toLowerCase();
        1476 if (key != lowerCase) {
        1477 this.remove(key);
        1478 this.setValues(lowerCase, value);
        1479 }
        1480 }, this);
        1481 }
        1482 this.ignoreCase_ = ignoreCase;
        1483};
        1484
        1485
        1486/**
        1487 * Extends a query data object with another query data or map like object. This
        1488 * operates 'in-place', it does not create a new QueryData object.
        1489 *
        1490 * @param {...(goog.Uri.QueryData|goog.structs.Map|Object)} var_args The object
        1491 * from which key value pairs will be copied.
        1492 */
        1493goog.Uri.QueryData.prototype.extend = function(var_args) {
        1494 for (var i = 0; i < arguments.length; i++) {
        1495 var data = arguments[i];
        1496 goog.structs.forEach(data,
        1497 /** @this {goog.Uri.QueryData} */
        1498 function(value, key) {
        1499 this.add(key, value);
        1500 }, this);
        1501 }
        1502};
        \ No newline at end of file +uri.js

        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
        32goog.provide('goog.Uri');
        33goog.provide('goog.Uri.QueryData');
        34
        35goog.require('goog.array');
        36goog.require('goog.string');
        37goog.require('goog.structs');
        38goog.require('goog.structs.Map');
        39goog.require('goog.uri.utils');
        40goog.require('goog.uri.utils.ComponentIndex');
        41goog.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 * Reserved characters (see RFC 3986 section 2.2) can be present in
        52 * their percent-encoded form in scheme, domain, and path URI components and
        53 * will not be auto-decoded. For example:
        54 * <code>goog.Uri.parse('rel%61tive/path%2fto/resource').getPath()</code> will
        55 * return <code>relative/path%2fto/resource</code>.
        56 *
        57 * The constructor accepts an optional unparsed, raw URI string. The parser
        58 * is relaxed, so special characters that aren't escaped but don't cause
        59 * ambiguities will not cause parse failures.
        60 *
        61 * All setters return <code>this</code> and so may be chained, a la
        62 * <code>goog.Uri.parse('/foo').setFragment('part').toString()</code>.
        63 *
        64 * @param {*=} opt_uri Optional string URI to parse
        65 * (use goog.Uri.create() to create a URI from parts), or if
        66 * a goog.Uri is passed, a clone is created.
        67 * @param {boolean=} opt_ignoreCase If true, #getParameterValue will ignore
        68 * the case of the parameter name.
        69 *
        70 * @throws URIError If opt_uri is provided and URI is malformed (that is,
        71 * if decodeURIComponent fails on any of the URI components).
        72 * @constructor
        73 * @struct
        74 */
        75goog.Uri = function(opt_uri, opt_ignoreCase) {
        76 /**
        77 * Scheme such as "http".
        78 * @private {string}
        79 */
        80 this.scheme_ = '';
        81
        82 /**
        83 * User credentials in the form "username:password".
        84 * @private {string}
        85 */
        86 this.userInfo_ = '';
        87
        88 /**
        89 * Domain part, e.g. "www.google.com".
        90 * @private {string}
        91 */
        92 this.domain_ = '';
        93
        94 /**
        95 * Port, e.g. 8080.
        96 * @private {?number}
        97 */
        98 this.port_ = null;
        99
        100 /**
        101 * Path, e.g. "/tests/img.png".
        102 * @private {string}
        103 */
        104 this.path_ = '';
        105
        106 /**
        107 * The fragment without the #.
        108 * @private {string}
        109 */
        110 this.fragment_ = '';
        111
        112 /**
        113 * Whether or not this Uri should be treated as Read Only.
        114 * @private {boolean}
        115 */
        116 this.isReadOnly_ = false;
        117
        118 /**
        119 * Whether or not to ignore case when comparing query params.
        120 * @private {boolean}
        121 */
        122 this.ignoreCase_ = false;
        123
        124 /**
        125 * Object representing query data.
        126 * @private {!goog.Uri.QueryData}
        127 */
        128 this.queryData_;
        129
        130 // Parse in the uri string
        131 var m;
        132 if (opt_uri instanceof goog.Uri) {
        133 this.ignoreCase_ = goog.isDef(opt_ignoreCase) ?
        134 opt_ignoreCase : opt_uri.getIgnoreCase();
        135 this.setScheme(opt_uri.getScheme());
        136 this.setUserInfo(opt_uri.getUserInfo());
        137 this.setDomain(opt_uri.getDomain());
        138 this.setPort(opt_uri.getPort());
        139 this.setPath(opt_uri.getPath());
        140 this.setQueryData(opt_uri.getQueryData().clone());
        141 this.setFragment(opt_uri.getFragment());
        142 } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) {
        143 this.ignoreCase_ = !!opt_ignoreCase;
        144
        145 // Set the parts -- decoding as we do so.
        146 // COMPATABILITY NOTE - In IE, unmatched fields may be empty strings,
        147 // whereas in other browsers they will be undefined.
        148 this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true);
        149 this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true);
        150 this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true);
        151 this.setPort(m[goog.uri.utils.ComponentIndex.PORT]);
        152 this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true);
        153 this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true);
        154 this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true);
        155
        156 } else {
        157 this.ignoreCase_ = !!opt_ignoreCase;
        158 this.queryData_ = new goog.Uri.QueryData(null, null, this.ignoreCase_);
        159 }
        160};
        161
        162
        163/**
        164 * If true, we preserve the type of query parameters set programmatically.
        165 *
        166 * This means that if you set a parameter to a boolean, and then call
        167 * getParameterValue, you will get a boolean back.
        168 *
        169 * If false, we will coerce parameters to strings, just as they would
        170 * appear in real URIs.
        171 *
        172 * TODO(nicksantos): Remove this once people have time to fix all tests.
        173 *
        174 * @type {boolean}
        175 */
        176goog.Uri.preserveParameterTypesCompatibilityFlag = false;
        177
        178
        179/**
        180 * Parameter name added to stop caching.
        181 * @type {string}
        182 */
        183goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;
        184
        185
        186/**
        187 * @return {string} The string form of the url.
        188 * @override
        189 */
        190goog.Uri.prototype.toString = function() {
        191 var out = [];
        192
        193 var scheme = this.getScheme();
        194 if (scheme) {
        195 out.push(goog.Uri.encodeSpecialChars_(
        196 scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), ':');
        197 }
        198
        199 var domain = this.getDomain();
        200 if (domain || scheme == 'file') {
        201 out.push('//');
        202
        203 var userInfo = this.getUserInfo();
        204 if (userInfo) {
        205 out.push(goog.Uri.encodeSpecialChars_(
        206 userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), '@');
        207 }
        208
        209 out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain)));
        210
        211 var port = this.getPort();
        212 if (port != null) {
        213 out.push(':', String(port));
        214 }
        215 }
        216
        217 var path = this.getPath();
        218 if (path) {
        219 if (this.hasDomain() && path.charAt(0) != '/') {
        220 out.push('/');
        221 }
        222 out.push(goog.Uri.encodeSpecialChars_(
        223 path,
        224 path.charAt(0) == '/' ?
        225 goog.Uri.reDisallowedInAbsolutePath_ :
        226 goog.Uri.reDisallowedInRelativePath_,
        227 true));
        228 }
        229
        230 var query = this.getEncodedQuery();
        231 if (query) {
        232 out.push('?', query);
        233 }
        234
        235 var fragment = this.getFragment();
        236 if (fragment) {
        237 out.push('#', goog.Uri.encodeSpecialChars_(
        238 fragment, goog.Uri.reDisallowedInFragment_));
        239 }
        240 return out.join('');
        241};
        242
        243
        244/**
        245 * Resolves the given relative URI (a goog.Uri object), using the URI
        246 * represented by this instance as the base URI.
        247 *
        248 * There are several kinds of relative URIs:<br>
        249 * 1. foo - replaces the last part of the path, the whole query and fragment<br>
        250 * 2. /foo - replaces the the path, the query and fragment<br>
        251 * 3. //foo - replaces everything from the domain on. foo is a domain name<br>
        252 * 4. ?foo - replace the query and fragment<br>
        253 * 5. #foo - replace the fragment only
        254 *
        255 * Additionally, if relative URI has a non-empty path, all ".." and "."
        256 * segments will be resolved, as described in RFC 3986.
        257 *
        258 * @param {!goog.Uri} relativeUri The relative URI to resolve.
        259 * @return {!goog.Uri} The resolved URI.
        260 */
        261goog.Uri.prototype.resolve = function(relativeUri) {
        262
        263 var absoluteUri = this.clone();
        264
        265 // we satisfy these conditions by looking for the first part of relativeUri
        266 // that is not blank and applying defaults to the rest
        267
        268 var overridden = relativeUri.hasScheme();
        269
        270 if (overridden) {
        271 absoluteUri.setScheme(relativeUri.getScheme());
        272 } else {
        273 overridden = relativeUri.hasUserInfo();
        274 }
        275
        276 if (overridden) {
        277 absoluteUri.setUserInfo(relativeUri.getUserInfo());
        278 } else {
        279 overridden = relativeUri.hasDomain();
        280 }
        281
        282 if (overridden) {
        283 absoluteUri.setDomain(relativeUri.getDomain());
        284 } else {
        285 overridden = relativeUri.hasPort();
        286 }
        287
        288 var path = relativeUri.getPath();
        289 if (overridden) {
        290 absoluteUri.setPort(relativeUri.getPort());
        291 } else {
        292 overridden = relativeUri.hasPath();
        293 if (overridden) {
        294 // resolve path properly
        295 if (path.charAt(0) != '/') {
        296 // path is relative
        297 if (this.hasDomain() && !this.hasPath()) {
        298 // RFC 3986, section 5.2.3, case 1
        299 path = '/' + path;
        300 } else {
        301 // RFC 3986, section 5.2.3, case 2
        302 var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/');
        303 if (lastSlashIndex != -1) {
        304 path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path;
        305 }
        306 }
        307 }
        308 path = goog.Uri.removeDotSegments(path);
        309 }
        310 }
        311
        312 if (overridden) {
        313 absoluteUri.setPath(path);
        314 } else {
        315 overridden = relativeUri.hasQuery();
        316 }
        317
        318 if (overridden) {
        319 absoluteUri.setQueryData(relativeUri.getDecodedQuery());
        320 } else {
        321 overridden = relativeUri.hasFragment();
        322 }
        323
        324 if (overridden) {
        325 absoluteUri.setFragment(relativeUri.getFragment());
        326 }
        327
        328 return absoluteUri;
        329};
        330
        331
        332/**
        333 * Clones the URI instance.
        334 * @return {!goog.Uri} New instance of the URI object.
        335 */
        336goog.Uri.prototype.clone = function() {
        337 return new goog.Uri(this);
        338};
        339
        340
        341/**
        342 * @return {string} The encoded scheme/protocol for the URI.
        343 */
        344goog.Uri.prototype.getScheme = function() {
        345 return this.scheme_;
        346};
        347
        348
        349/**
        350 * Sets the scheme/protocol.
        351 * @throws URIError If opt_decode is true and newScheme is malformed (that is,
        352 * if decodeURIComponent fails).
        353 * @param {string} newScheme New scheme value.
        354 * @param {boolean=} opt_decode Optional param for whether to decode new value.
        355 * @return {!goog.Uri} Reference to this URI object.
        356 */
        357goog.Uri.prototype.setScheme = function(newScheme, opt_decode) {
        358 this.enforceReadOnly();
        359 this.scheme_ = opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) :
        360 newScheme;
        361
        362 // remove an : at the end of the scheme so somebody can pass in
        363 // window.location.protocol
        364 if (this.scheme_) {
        365 this.scheme_ = this.scheme_.replace(/:$/, '');
        366 }
        367 return this;
        368};
        369
        370
        371/**
        372 * @return {boolean} Whether the scheme has been set.
        373 */
        374goog.Uri.prototype.hasScheme = function() {
        375 return !!this.scheme_;
        376};
        377
        378
        379/**
        380 * @return {string} The decoded user info.
        381 */
        382goog.Uri.prototype.getUserInfo = function() {
        383 return this.userInfo_;
        384};
        385
        386
        387/**
        388 * Sets the userInfo.
        389 * @throws URIError If opt_decode is true and newUserInfo is malformed (that is,
        390 * if decodeURIComponent fails).
        391 * @param {string} newUserInfo New userInfo value.
        392 * @param {boolean=} opt_decode Optional param for whether to decode new value.
        393 * @return {!goog.Uri} Reference to this URI object.
        394 */
        395goog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) {
        396 this.enforceReadOnly();
        397 this.userInfo_ = opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) :
        398 newUserInfo;
        399 return this;
        400};
        401
        402
        403/**
        404 * @return {boolean} Whether the user info has been set.
        405 */
        406goog.Uri.prototype.hasUserInfo = function() {
        407 return !!this.userInfo_;
        408};
        409
        410
        411/**
        412 * @return {string} The decoded domain.
        413 */
        414goog.Uri.prototype.getDomain = function() {
        415 return this.domain_;
        416};
        417
        418
        419/**
        420 * Sets the domain.
        421 * @throws URIError If opt_decode is true and newDomain is malformed (that is,
        422 * if decodeURIComponent fails).
        423 * @param {string} newDomain New domain value.
        424 * @param {boolean=} opt_decode Optional param for whether to decode new value.
        425 * @return {!goog.Uri} Reference to this URI object.
        426 */
        427goog.Uri.prototype.setDomain = function(newDomain, opt_decode) {
        428 this.enforceReadOnly();
        429 this.domain_ = opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) :
        430 newDomain;
        431 return this;
        432};
        433
        434
        435/**
        436 * @return {boolean} Whether the domain has been set.
        437 */
        438goog.Uri.prototype.hasDomain = function() {
        439 return !!this.domain_;
        440};
        441
        442
        443/**
        444 * @return {?number} The port number.
        445 */
        446goog.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 */
        456goog.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 */
        476goog.Uri.prototype.hasPort = function() {
        477 return this.port_ != null;
        478};
        479
        480
        481/**
        482 * @return {string} The decoded path.
        483 */
        484goog.Uri.prototype.getPath = function() {
        485 return this.path_;
        486};
        487
        488
        489/**
        490 * Sets the path.
        491 * @throws URIError If opt_decode is true and newPath is malformed (that is,
        492 * if decodeURIComponent fails).
        493 * @param {string} newPath New path value.
        494 * @param {boolean=} opt_decode Optional param for whether to decode new value.
        495 * @return {!goog.Uri} Reference to this URI object.
        496 */
        497goog.Uri.prototype.setPath = function(newPath, opt_decode) {
        498 this.enforceReadOnly();
        499 this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath;
        500 return this;
        501};
        502
        503
        504/**
        505 * @return {boolean} Whether the path has been set.
        506 */
        507goog.Uri.prototype.hasPath = function() {
        508 return !!this.path_;
        509};
        510
        511
        512/**
        513 * @return {boolean} Whether the query string has been set.
        514 */
        515goog.Uri.prototype.hasQuery = function() {
        516 return this.queryData_.toString() !== '';
        517};
        518
        519
        520/**
        521 * Sets the query data.
        522 * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object.
        523 * @param {boolean=} opt_decode Optional param for whether to decode new value.
        524 * Applies only if queryData is a string.
        525 * @return {!goog.Uri} Reference to this URI object.
        526 */
        527goog.Uri.prototype.setQueryData = function(queryData, opt_decode) {
        528 this.enforceReadOnly();
        529
        530 if (queryData instanceof goog.Uri.QueryData) {
        531 this.queryData_ = queryData;
        532 this.queryData_.setIgnoreCase(this.ignoreCase_);
        533 } else {
        534 if (!opt_decode) {
        535 // QueryData accepts encoded query string, so encode it if
        536 // opt_decode flag is not true.
        537 queryData = goog.Uri.encodeSpecialChars_(queryData,
        538 goog.Uri.reDisallowedInQuery_);
        539 }
        540 this.queryData_ = new goog.Uri.QueryData(queryData, null, this.ignoreCase_);
        541 }
        542
        543 return this;
        544};
        545
        546
        547/**
        548 * Sets the URI query.
        549 * @param {string} newQuery New query value.
        550 * @param {boolean=} opt_decode Optional param for whether to decode new value.
        551 * @return {!goog.Uri} Reference to this URI object.
        552 */
        553goog.Uri.prototype.setQuery = function(newQuery, opt_decode) {
        554 return this.setQueryData(newQuery, opt_decode);
        555};
        556
        557
        558/**
        559 * @return {string} The encoded URI query, not including the ?.
        560 */
        561goog.Uri.prototype.getEncodedQuery = function() {
        562 return this.queryData_.toString();
        563};
        564
        565
        566/**
        567 * @return {string} The decoded URI query, not including the ?.
        568 */
        569goog.Uri.prototype.getDecodedQuery = function() {
        570 return this.queryData_.toDecodedString();
        571};
        572
        573
        574/**
        575 * Returns the query data.
        576 * @return {!goog.Uri.QueryData} QueryData object.
        577 */
        578goog.Uri.prototype.getQueryData = function() {
        579 return this.queryData_;
        580};
        581
        582
        583/**
        584 * @return {string} The encoded URI query, not including the ?.
        585 *
        586 * Warning: This method, unlike other getter methods, returns encoded
        587 * value, instead of decoded one.
        588 */
        589goog.Uri.prototype.getQuery = function() {
        590 return this.getEncodedQuery();
        591};
        592
        593
        594/**
        595 * Sets the value of the named query parameters, clearing previous values for
        596 * that key.
        597 *
        598 * @param {string} key The parameter to set.
        599 * @param {*} value The new value.
        600 * @return {!goog.Uri} Reference to this URI object.
        601 */
        602goog.Uri.prototype.setParameterValue = function(key, value) {
        603 this.enforceReadOnly();
        604 this.queryData_.set(key, value);
        605 return this;
        606};
        607
        608
        609/**
        610 * Sets the values of the named query parameters, clearing previous values for
        611 * that key. Not new values will currently be moved to the end of the query
        612 * string.
        613 *
        614 * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
        615 * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p>
        616 *
        617 * @param {string} key The parameter to set.
        618 * @param {*} values The new values. If values is a single
        619 * string then it will be treated as the sole value.
        620 * @return {!goog.Uri} Reference to this URI object.
        621 */
        622goog.Uri.prototype.setParameterValues = function(key, values) {
        623 this.enforceReadOnly();
        624
        625 if (!goog.isArray(values)) {
        626 values = [String(values)];
        627 }
        628
        629 this.queryData_.setValues(key, values);
        630
        631 return this;
        632};
        633
        634
        635/**
        636 * Returns the value<b>s</b> for a given cgi parameter as a list of decoded
        637 * query parameter values.
        638 * @param {string} name The parameter to get values for.
        639 * @return {!Array<?>} The values for a given cgi parameter as a list of
        640 * decoded query parameter values.
        641 */
        642goog.Uri.prototype.getParameterValues = function(name) {
        643 return this.queryData_.getValues(name);
        644};
        645
        646
        647/**
        648 * Returns the first value for a given cgi parameter or undefined if the given
        649 * parameter name does not appear in the query string.
        650 * @param {string} paramName Unescaped parameter name.
        651 * @return {string|undefined} The first value for a given cgi parameter or
        652 * undefined if the given parameter name does not appear in the query
        653 * string.
        654 */
        655goog.Uri.prototype.getParameterValue = function(paramName) {
        656 // NOTE(nicksantos): This type-cast is a lie when
        657 // preserveParameterTypesCompatibilityFlag is set to true.
        658 // But this should only be set to true in tests.
        659 return /** @type {string|undefined} */ (this.queryData_.get(paramName));
        660};
        661
        662
        663/**
        664 * @return {string} The URI fragment, not including the #.
        665 */
        666goog.Uri.prototype.getFragment = function() {
        667 return this.fragment_;
        668};
        669
        670
        671/**
        672 * Sets the URI fragment.
        673 * @throws URIError If opt_decode is true and newFragment is malformed (that is,
        674 * if decodeURIComponent fails).
        675 * @param {string} newFragment New fragment value.
        676 * @param {boolean=} opt_decode Optional param for whether to decode new value.
        677 * @return {!goog.Uri} Reference to this URI object.
        678 */
        679goog.Uri.prototype.setFragment = function(newFragment, opt_decode) {
        680 this.enforceReadOnly();
        681 this.fragment_ = opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) :
        682 newFragment;
        683 return this;
        684};
        685
        686
        687/**
        688 * @return {boolean} Whether the URI has a fragment set.
        689 */
        690goog.Uri.prototype.hasFragment = function() {
        691 return !!this.fragment_;
        692};
        693
        694
        695/**
        696 * Returns true if this has the same domain as that of uri2.
        697 * @param {!goog.Uri} uri2 The URI object to compare to.
        698 * @return {boolean} true if same domain; false otherwise.
        699 */
        700goog.Uri.prototype.hasSameDomainAs = function(uri2) {
        701 return ((!this.hasDomain() && !uri2.hasDomain()) ||
        702 this.getDomain() == uri2.getDomain()) &&
        703 ((!this.hasPort() && !uri2.hasPort()) ||
        704 this.getPort() == uri2.getPort());
        705};
        706
        707
        708/**
        709 * Adds a random parameter to the Uri.
        710 * @return {!goog.Uri} Reference to this Uri object.
        711 */
        712goog.Uri.prototype.makeUnique = function() {
        713 this.enforceReadOnly();
        714 this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString());
        715
        716 return this;
        717};
        718
        719
        720/**
        721 * Removes the named query parameter.
        722 *
        723 * @param {string} key The parameter to remove.
        724 * @return {!goog.Uri} Reference to this URI object.
        725 */
        726goog.Uri.prototype.removeParameter = function(key) {
        727 this.enforceReadOnly();
        728 this.queryData_.remove(key);
        729 return this;
        730};
        731
        732
        733/**
        734 * Sets whether Uri is read only. If this goog.Uri is read-only,
        735 * enforceReadOnly_ will be called at the start of any function that may modify
        736 * this Uri.
        737 * @param {boolean} isReadOnly whether this goog.Uri should be read only.
        738 * @return {!goog.Uri} Reference to this Uri object.
        739 */
        740goog.Uri.prototype.setReadOnly = function(isReadOnly) {
        741 this.isReadOnly_ = isReadOnly;
        742 return this;
        743};
        744
        745
        746/**
        747 * @return {boolean} Whether the URI is read only.
        748 */
        749goog.Uri.prototype.isReadOnly = function() {
        750 return this.isReadOnly_;
        751};
        752
        753
        754/**
        755 * Checks if this Uri has been marked as read only, and if so, throws an error.
        756 * This should be called whenever any modifying function is called.
        757 */
        758goog.Uri.prototype.enforceReadOnly = function() {
        759 if (this.isReadOnly_) {
        760 throw Error('Tried to modify a read-only Uri');
        761 }
        762};
        763
        764
        765/**
        766 * Sets whether to ignore case.
        767 * NOTE: If there are already key/value pairs in the QueryData, and
        768 * ignoreCase_ is set to false, the keys will all be lower-cased.
        769 * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
        770 * @return {!goog.Uri} Reference to this Uri object.
        771 */
        772goog.Uri.prototype.setIgnoreCase = function(ignoreCase) {
        773 this.ignoreCase_ = ignoreCase;
        774 if (this.queryData_) {
        775 this.queryData_.setIgnoreCase(ignoreCase);
        776 }
        777 return this;
        778};
        779
        780
        781/**
        782 * @return {boolean} Whether to ignore case.
        783 */
        784goog.Uri.prototype.getIgnoreCase = function() {
        785 return this.ignoreCase_;
        786};
        787
        788
        789//==============================================================================
        790// Static members
        791//==============================================================================
        792
        793
        794/**
        795 * Creates a uri from the string form. Basically an alias of new goog.Uri().
        796 * If a Uri object is passed to parse then it will return a clone of the object.
        797 *
        798 * @throws URIError If parsing the URI is malformed. The passed URI components
        799 * should all be parseable by decodeURIComponent.
        800 * @param {*} uri Raw URI string or instance of Uri
        801 * object.
        802 * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter
        803 * names in #getParameterValue.
        804 * @return {!goog.Uri} The new URI object.
        805 */
        806goog.Uri.parse = function(uri, opt_ignoreCase) {
        807 return uri instanceof goog.Uri ?
        808 uri.clone() : new goog.Uri(uri, opt_ignoreCase);
        809};
        810
        811
        812/**
        813 * Creates a new goog.Uri object from unencoded parts.
        814 *
        815 * @param {?string=} opt_scheme Scheme/protocol or full URI to parse.
        816 * @param {?string=} opt_userInfo username:password.
        817 * @param {?string=} opt_domain www.google.com.
        818 * @param {?number=} opt_port 9830.
        819 * @param {?string=} opt_path /some/path/to/a/file.html.
        820 * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2.
        821 * @param {?string=} opt_fragment The fragment without the #.
        822 * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in
        823 * #getParameterValue.
        824 *
        825 * @return {!goog.Uri} The new URI object.
        826 */
        827goog.Uri.create = function(opt_scheme, opt_userInfo, opt_domain, opt_port,
        828 opt_path, opt_query, opt_fragment, opt_ignoreCase) {
        829
        830 var uri = new goog.Uri(null, opt_ignoreCase);
        831
        832 // Only set the parts if they are defined and not empty strings.
        833 opt_scheme && uri.setScheme(opt_scheme);
        834 opt_userInfo && uri.setUserInfo(opt_userInfo);
        835 opt_domain && uri.setDomain(opt_domain);
        836 opt_port && uri.setPort(opt_port);
        837 opt_path && uri.setPath(opt_path);
        838 opt_query && uri.setQueryData(opt_query);
        839 opt_fragment && uri.setFragment(opt_fragment);
        840
        841 return uri;
        842};
        843
        844
        845/**
        846 * Resolves a relative Uri against a base Uri, accepting both strings and
        847 * Uri objects.
        848 *
        849 * @param {*} base Base Uri.
        850 * @param {*} rel Relative Uri.
        851 * @return {!goog.Uri} Resolved uri.
        852 */
        853goog.Uri.resolve = function(base, rel) {
        854 if (!(base instanceof goog.Uri)) {
        855 base = goog.Uri.parse(base);
        856 }
        857
        858 if (!(rel instanceof goog.Uri)) {
        859 rel = goog.Uri.parse(rel);
        860 }
        861
        862 return base.resolve(rel);
        863};
        864
        865
        866/**
        867 * Removes dot segments in given path component, as described in
        868 * RFC 3986, section 5.2.4.
        869 *
        870 * @param {string} path A non-empty path component.
        871 * @return {string} Path component with removed dot segments.
        872 */
        873goog.Uri.removeDotSegments = function(path) {
        874 if (path == '..' || path == '.') {
        875 return '';
        876
        877 } else if (!goog.string.contains(path, './') &&
        878 !goog.string.contains(path, '/.')) {
        879 // This optimization detects uris which do not contain dot-segments,
        880 // and as a consequence do not require any processing.
        881 return path;
        882
        883 } else {
        884 var leadingSlash = goog.string.startsWith(path, '/');
        885 var segments = path.split('/');
        886 var out = [];
        887
        888 for (var pos = 0; pos < segments.length; ) {
        889 var segment = segments[pos++];
        890
        891 if (segment == '.') {
        892 if (leadingSlash && pos == segments.length) {
        893 out.push('');
        894 }
        895 } else if (segment == '..') {
        896 if (out.length > 1 || out.length == 1 && out[0] != '') {
        897 out.pop();
        898 }
        899 if (leadingSlash && pos == segments.length) {
        900 out.push('');
        901 }
        902 } else {
        903 out.push(segment);
        904 leadingSlash = true;
        905 }
        906 }
        907
        908 return out.join('/');
        909 }
        910};
        911
        912
        913/**
        914 * Decodes a value or returns the empty string if it isn't defined or empty.
        915 * @throws URIError If decodeURIComponent fails to decode val.
        916 * @param {string|undefined} val Value to decode.
        917 * @param {boolean=} opt_preserveReserved If true, restricted characters will
        918 * not be decoded.
        919 * @return {string} Decoded value.
        920 * @private
        921 */
        922goog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) {
        923 // Don't use UrlDecode() here because val is not a query parameter.
        924 if (!val) {
        925 return '';
        926 }
        927
        928 // decodeURI has the same output for '%2f' and '%252f'. We double encode %25
        929 // so that we can distinguish between the 2 inputs. This is later undone by
        930 // removeDoubleEncoding_.
        931 return opt_preserveReserved ?
        932 decodeURI(val.replace(/%25/g, '%2525')) : decodeURIComponent(val);
        933};
        934
        935
        936/**
        937 * If unescapedPart is non null, then escapes any characters in it that aren't
        938 * valid characters in a url and also escapes any special characters that
        939 * appear in extra.
        940 *
        941 * @param {*} unescapedPart The string to encode.
        942 * @param {RegExp} extra A character set of characters in [\01-\177].
        943 * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent
        944 * encoding.
        945 * @return {?string} null iff unescapedPart == null.
        946 * @private
        947 */
        948goog.Uri.encodeSpecialChars_ = function(unescapedPart, extra,
        949 opt_removeDoubleEncoding) {
        950 if (goog.isString(unescapedPart)) {
        951 var encoded = encodeURI(unescapedPart).
        952 replace(extra, goog.Uri.encodeChar_);
        953 if (opt_removeDoubleEncoding) {
        954 // encodeURI double-escapes %XX sequences used to represent restricted
        955 // characters in some URI components, remove the double escaping here.
        956 encoded = goog.Uri.removeDoubleEncoding_(encoded);
        957 }
        958 return encoded;
        959 }
        960 return null;
        961};
        962
        963
        964/**
        965 * Converts a character in [\01-\177] to its unicode character equivalent.
        966 * @param {string} ch One character string.
        967 * @return {string} Encoded string.
        968 * @private
        969 */
        970goog.Uri.encodeChar_ = function(ch) {
        971 var n = ch.charCodeAt(0);
        972 return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16);
        973};
        974
        975
        976/**
        977 * Removes double percent-encoding from a string.
        978 * @param {string} doubleEncodedString String
        979 * @return {string} String with double encoding removed.
        980 * @private
        981 */
        982goog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) {
        983 return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1');
        984};
        985
        986
        987/**
        988 * Regular expression for characters that are disallowed in the scheme or
        989 * userInfo part of the URI.
        990 * @type {RegExp}
        991 * @private
        992 */
        993goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g;
        994
        995
        996/**
        997 * Regular expression for characters that are disallowed in a relative path.
        998 * Colon is included due to RFC 3986 3.3.
        999 * @type {RegExp}
        1000 * @private
        1001 */
        1002goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g;
        1003
        1004
        1005/**
        1006 * Regular expression for characters that are disallowed in an absolute path.
        1007 * @type {RegExp}
        1008 * @private
        1009 */
        1010goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g;
        1011
        1012
        1013/**
        1014 * Regular expression for characters that are disallowed in the query.
        1015 * @type {RegExp}
        1016 * @private
        1017 */
        1018goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g;
        1019
        1020
        1021/**
        1022 * Regular expression for characters that are disallowed in the fragment.
        1023 * @type {RegExp}
        1024 * @private
        1025 */
        1026goog.Uri.reDisallowedInFragment_ = /#/g;
        1027
        1028
        1029/**
        1030 * Checks whether two URIs have the same domain.
        1031 * @param {string} uri1String First URI string.
        1032 * @param {string} uri2String Second URI string.
        1033 * @return {boolean} true if the two URIs have the same domain; false otherwise.
        1034 */
        1035goog.Uri.haveSameDomain = function(uri1String, uri2String) {
        1036 // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme.
        1037 // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain.
        1038 var pieces1 = goog.uri.utils.split(uri1String);
        1039 var pieces2 = goog.uri.utils.split(uri2String);
        1040 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
        1041 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
        1042 pieces1[goog.uri.utils.ComponentIndex.PORT] ==
        1043 pieces2[goog.uri.utils.ComponentIndex.PORT];
        1044};
        1045
        1046
        1047
        1048/**
        1049 * Class used to represent URI query parameters. It is essentially a hash of
        1050 * name-value pairs, though a name can be present more than once.
        1051 *
        1052 * Has the same interface as the collections in goog.structs.
        1053 *
        1054 * @param {?string=} opt_query Optional encoded query string to parse into
        1055 * the object.
        1056 * @param {goog.Uri=} opt_uri Optional uri object that should have its
        1057 * cache invalidated when this object updates. Deprecated -- this
        1058 * is no longer required.
        1059 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
        1060 * name in #get.
        1061 * @constructor
        1062 * @struct
        1063 * @final
        1064 */
        1065goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) {
        1066 /**
        1067 * The map containing name/value or name/array-of-values pairs.
        1068 * May be null if it requires parsing from the query string.
        1069 *
        1070 * We need to use a Map because we cannot guarantee that the key names will
        1071 * not be problematic for IE.
        1072 *
        1073 * @private {goog.structs.Map<string, !Array<*>>}
        1074 */
        1075 this.keyMap_ = null;
        1076
        1077 /**
        1078 * The number of params, or null if it requires computing.
        1079 * @private {?number}
        1080 */
        1081 this.count_ = null;
        1082
        1083 /**
        1084 * Encoded query string, or null if it requires computing from the key map.
        1085 * @private {?string}
        1086 */
        1087 this.encodedQuery_ = opt_query || null;
        1088
        1089 /**
        1090 * If true, ignore the case of the parameter name in #get.
        1091 * @private {boolean}
        1092 */
        1093 this.ignoreCase_ = !!opt_ignoreCase;
        1094};
        1095
        1096
        1097/**
        1098 * If the underlying key map is not yet initialized, it parses the
        1099 * query string and fills the map with parsed data.
        1100 * @private
        1101 */
        1102goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {
        1103 if (!this.keyMap_) {
        1104 this.keyMap_ = new goog.structs.Map();
        1105 this.count_ = 0;
        1106 if (this.encodedQuery_) {
        1107 var self = this;
        1108 goog.uri.utils.parseQueryData(this.encodedQuery_, function(name, value) {
        1109 self.add(goog.string.urlDecode(name), value);
        1110 });
        1111 }
        1112 }
        1113};
        1114
        1115
        1116/**
        1117 * Creates a new query data instance from a map of names and values.
        1118 *
        1119 * @param {!goog.structs.Map<string, ?>|!Object} map Map of string parameter
        1120 * names to parameter value. If parameter value is an array, it is
        1121 * treated as if the key maps to each individual value in the
        1122 * array.
        1123 * @param {goog.Uri=} opt_uri URI object that should have its cache
        1124 * invalidated when this object updates.
        1125 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
        1126 * name in #get.
        1127 * @return {!goog.Uri.QueryData} The populated query data instance.
        1128 */
        1129goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) {
        1130 var keys = goog.structs.getKeys(map);
        1131 if (typeof keys == 'undefined') {
        1132 throw Error('Keys are undefined');
        1133 }
        1134
        1135 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
        1136 var values = goog.structs.getValues(map);
        1137 for (var i = 0; i < keys.length; i++) {
        1138 var key = keys[i];
        1139 var value = values[i];
        1140 if (!goog.isArray(value)) {
        1141 queryData.add(key, value);
        1142 } else {
        1143 queryData.setValues(key, value);
        1144 }
        1145 }
        1146 return queryData;
        1147};
        1148
        1149
        1150/**
        1151 * Creates a new query data instance from parallel arrays of parameter names
        1152 * and values. Allows for duplicate parameter names. Throws an error if the
        1153 * lengths of the arrays differ.
        1154 *
        1155 * @param {!Array<string>} keys Parameter names.
        1156 * @param {!Array<?>} values Parameter values.
        1157 * @param {goog.Uri=} opt_uri URI object that should have its cache
        1158 * invalidated when this object updates.
        1159 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
        1160 * name in #get.
        1161 * @return {!goog.Uri.QueryData} The populated query data instance.
        1162 */
        1163goog.Uri.QueryData.createFromKeysValues = function(
        1164 keys, values, opt_uri, opt_ignoreCase) {
        1165 if (keys.length != values.length) {
        1166 throw Error('Mismatched lengths for keys/values');
        1167 }
        1168 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
        1169 for (var i = 0; i < keys.length; i++) {
        1170 queryData.add(keys[i], values[i]);
        1171 }
        1172 return queryData;
        1173};
        1174
        1175
        1176/**
        1177 * @return {?number} The number of parameters.
        1178 */
        1179goog.Uri.QueryData.prototype.getCount = function() {
        1180 this.ensureKeyMapInitialized_();
        1181 return this.count_;
        1182};
        1183
        1184
        1185/**
        1186 * Adds a key value pair.
        1187 * @param {string} key Name.
        1188 * @param {*} value Value.
        1189 * @return {!goog.Uri.QueryData} Instance of this object.
        1190 */
        1191goog.Uri.QueryData.prototype.add = function(key, value) {
        1192 this.ensureKeyMapInitialized_();
        1193 this.invalidateCache_();
        1194
        1195 key = this.getKeyName_(key);
        1196 var values = this.keyMap_.get(key);
        1197 if (!values) {
        1198 this.keyMap_.set(key, (values = []));
        1199 }
        1200 values.push(value);
        1201 this.count_++;
        1202 return this;
        1203};
        1204
        1205
        1206/**
        1207 * Removes all the params with the given key.
        1208 * @param {string} key Name.
        1209 * @return {boolean} Whether any parameter was removed.
        1210 */
        1211goog.Uri.QueryData.prototype.remove = function(key) {
        1212 this.ensureKeyMapInitialized_();
        1213
        1214 key = this.getKeyName_(key);
        1215 if (this.keyMap_.containsKey(key)) {
        1216 this.invalidateCache_();
        1217
        1218 // Decrement parameter count.
        1219 this.count_ -= this.keyMap_.get(key).length;
        1220 return this.keyMap_.remove(key);
        1221 }
        1222 return false;
        1223};
        1224
        1225
        1226/**
        1227 * Clears the parameters.
        1228 */
        1229goog.Uri.QueryData.prototype.clear = function() {
        1230 this.invalidateCache_();
        1231 this.keyMap_ = null;
        1232 this.count_ = 0;
        1233};
        1234
        1235
        1236/**
        1237 * @return {boolean} Whether we have any parameters.
        1238 */
        1239goog.Uri.QueryData.prototype.isEmpty = function() {
        1240 this.ensureKeyMapInitialized_();
        1241 return this.count_ == 0;
        1242};
        1243
        1244
        1245/**
        1246 * Whether there is a parameter with the given name
        1247 * @param {string} key The parameter name to check for.
        1248 * @return {boolean} Whether there is a parameter with the given name.
        1249 */
        1250goog.Uri.QueryData.prototype.containsKey = function(key) {
        1251 this.ensureKeyMapInitialized_();
        1252 key = this.getKeyName_(key);
        1253 return this.keyMap_.containsKey(key);
        1254};
        1255
        1256
        1257/**
        1258 * Whether there is a parameter with the given value.
        1259 * @param {*} value The value to check for.
        1260 * @return {boolean} Whether there is a parameter with the given value.
        1261 */
        1262goog.Uri.QueryData.prototype.containsValue = function(value) {
        1263 // NOTE(arv): This solution goes through all the params even if it was the
        1264 // first param. We can get around this by not reusing code or by switching to
        1265 // iterators.
        1266 var vals = this.getValues();
        1267 return goog.array.contains(vals, value);
        1268};
        1269
        1270
        1271/**
        1272 * Returns all the keys of the parameters. If a key is used multiple times
        1273 * it will be included multiple times in the returned array
        1274 * @return {!Array<string>} All the keys of the parameters.
        1275 */
        1276goog.Uri.QueryData.prototype.getKeys = function() {
        1277 this.ensureKeyMapInitialized_();
        1278 // We need to get the values to know how many keys to add.
        1279 var vals = /** @type {!Array<*>} */ (this.keyMap_.getValues());
        1280 var keys = this.keyMap_.getKeys();
        1281 var rv = [];
        1282 for (var i = 0; i < keys.length; i++) {
        1283 var val = vals[i];
        1284 for (var j = 0; j < val.length; j++) {
        1285 rv.push(keys[i]);
        1286 }
        1287 }
        1288 return rv;
        1289};
        1290
        1291
        1292/**
        1293 * Returns all the values of the parameters with the given name. If the query
        1294 * data has no such key this will return an empty array. If no key is given
        1295 * all values wil be returned.
        1296 * @param {string=} opt_key The name of the parameter to get the values for.
        1297 * @return {!Array<?>} All the values of the parameters with the given name.
        1298 */
        1299goog.Uri.QueryData.prototype.getValues = function(opt_key) {
        1300 this.ensureKeyMapInitialized_();
        1301 var rv = [];
        1302 if (goog.isString(opt_key)) {
        1303 if (this.containsKey(opt_key)) {
        1304 rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));
        1305 }
        1306 } else {
        1307 // Return all values.
        1308 var values = this.keyMap_.getValues();
        1309 for (var i = 0; i < values.length; i++) {
        1310 rv = goog.array.concat(rv, values[i]);
        1311 }
        1312 }
        1313 return rv;
        1314};
        1315
        1316
        1317/**
        1318 * Sets a key value pair and removes all other keys with the same value.
        1319 *
        1320 * @param {string} key Name.
        1321 * @param {*} value Value.
        1322 * @return {!goog.Uri.QueryData} Instance of this object.
        1323 */
        1324goog.Uri.QueryData.prototype.set = function(key, value) {
        1325 this.ensureKeyMapInitialized_();
        1326 this.invalidateCache_();
        1327
        1328 // TODO(chrishenry): This could be better written as
        1329 // this.remove(key), this.add(key, value), but that would reorder
        1330 // the key (since the key is first removed and then added at the
        1331 // end) and we would have to fix unit tests that depend on key
        1332 // ordering.
        1333 key = this.getKeyName_(key);
        1334 if (this.containsKey(key)) {
        1335 this.count_ -= this.keyMap_.get(key).length;
        1336 }
        1337 this.keyMap_.set(key, [value]);
        1338 this.count_++;
        1339 return this;
        1340};
        1341
        1342
        1343/**
        1344 * Returns the first value associated with the key. If the query data has no
        1345 * such key this will return undefined or the optional default.
        1346 * @param {string} key The name of the parameter to get the value for.
        1347 * @param {*=} opt_default The default value to return if the query data
        1348 * has no such key.
        1349 * @return {*} The first string value associated with the key, or opt_default
        1350 * if there's no value.
        1351 */
        1352goog.Uri.QueryData.prototype.get = function(key, opt_default) {
        1353 var values = key ? this.getValues(key) : [];
        1354 if (goog.Uri.preserveParameterTypesCompatibilityFlag) {
        1355 return values.length > 0 ? values[0] : opt_default;
        1356 } else {
        1357 return values.length > 0 ? String(values[0]) : opt_default;
        1358 }
        1359};
        1360
        1361
        1362/**
        1363 * Sets the values for a key. If the key already exists, this will
        1364 * override all of the existing values that correspond to the key.
        1365 * @param {string} key The key to set values for.
        1366 * @param {!Array<?>} values The values to set.
        1367 */
        1368goog.Uri.QueryData.prototype.setValues = function(key, values) {
        1369 this.remove(key);
        1370
        1371 if (values.length > 0) {
        1372 this.invalidateCache_();
        1373 this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));
        1374 this.count_ += values.length;
        1375 }
        1376};
        1377
        1378
        1379/**
        1380 * @return {string} Encoded query string.
        1381 * @override
        1382 */
        1383goog.Uri.QueryData.prototype.toString = function() {
        1384 if (this.encodedQuery_) {
        1385 return this.encodedQuery_;
        1386 }
        1387
        1388 if (!this.keyMap_) {
        1389 return '';
        1390 }
        1391
        1392 var sb = [];
        1393
        1394 // In the past, we use this.getKeys() and this.getVals(), but that
        1395 // generates a lot of allocations as compared to simply iterating
        1396 // over the keys.
        1397 var keys = this.keyMap_.getKeys();
        1398 for (var i = 0; i < keys.length; i++) {
        1399 var key = keys[i];
        1400 var encodedKey = goog.string.urlEncode(key);
        1401 var val = this.getValues(key);
        1402 for (var j = 0; j < val.length; j++) {
        1403 var param = encodedKey;
        1404 // Ensure that null and undefined are encoded into the url as
        1405 // literal strings.
        1406 if (val[j] !== '') {
        1407 param += '=' + goog.string.urlEncode(val[j]);
        1408 }
        1409 sb.push(param);
        1410 }
        1411 }
        1412
        1413 return this.encodedQuery_ = sb.join('&');
        1414};
        1415
        1416
        1417/**
        1418 * @throws URIError If URI is malformed (that is, if decodeURIComponent fails on
        1419 * any of the URI components).
        1420 * @return {string} Decoded query string.
        1421 */
        1422goog.Uri.QueryData.prototype.toDecodedString = function() {
        1423 return goog.Uri.decodeOrEmpty_(this.toString());
        1424};
        1425
        1426
        1427/**
        1428 * Invalidate the cache.
        1429 * @private
        1430 */
        1431goog.Uri.QueryData.prototype.invalidateCache_ = function() {
        1432 this.encodedQuery_ = null;
        1433};
        1434
        1435
        1436/**
        1437 * Removes all keys that are not in the provided list. (Modifies this object.)
        1438 * @param {Array<string>} keys The desired keys.
        1439 * @return {!goog.Uri.QueryData} a reference to this object.
        1440 */
        1441goog.Uri.QueryData.prototype.filterKeys = function(keys) {
        1442 this.ensureKeyMapInitialized_();
        1443 this.keyMap_.forEach(
        1444 function(value, key) {
        1445 if (!goog.array.contains(keys, key)) {
        1446 this.remove(key);
        1447 }
        1448 }, this);
        1449 return this;
        1450};
        1451
        1452
        1453/**
        1454 * Clone the query data instance.
        1455 * @return {!goog.Uri.QueryData} New instance of the QueryData object.
        1456 */
        1457goog.Uri.QueryData.prototype.clone = function() {
        1458 var rv = new goog.Uri.QueryData();
        1459 rv.encodedQuery_ = this.encodedQuery_;
        1460 if (this.keyMap_) {
        1461 rv.keyMap_ = this.keyMap_.clone();
        1462 rv.count_ = this.count_;
        1463 }
        1464 return rv;
        1465};
        1466
        1467
        1468/**
        1469 * Helper function to get the key name from a JavaScript object. Converts
        1470 * the object to a string, and to lower case if necessary.
        1471 * @private
        1472 * @param {*} arg The object to get a key name from.
        1473 * @return {string} valid key name which can be looked up in #keyMap_.
        1474 */
        1475goog.Uri.QueryData.prototype.getKeyName_ = function(arg) {
        1476 var keyName = String(arg);
        1477 if (this.ignoreCase_) {
        1478 keyName = keyName.toLowerCase();
        1479 }
        1480 return keyName;
        1481};
        1482
        1483
        1484/**
        1485 * Ignore case in parameter names.
        1486 * NOTE: If there are already key/value pairs in the QueryData, and
        1487 * ignoreCase_ is set to false, the keys will all be lower-cased.
        1488 * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
        1489 */
        1490goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {
        1491 var resetKeys = ignoreCase && !this.ignoreCase_;
        1492 if (resetKeys) {
        1493 this.ensureKeyMapInitialized_();
        1494 this.invalidateCache_();
        1495 this.keyMap_.forEach(
        1496 function(value, key) {
        1497 var lowerCase = key.toLowerCase();
        1498 if (key != lowerCase) {
        1499 this.remove(key);
        1500 this.setValues(lowerCase, value);
        1501 }
        1502 }, this);
        1503 }
        1504 this.ignoreCase_ = ignoreCase;
        1505};
        1506
        1507
        1508/**
        1509 * Extends a query data object with another query data or map like object. This
        1510 * operates 'in-place', it does not create a new QueryData object.
        1511 *
        1512 * @param {...(goog.Uri.QueryData|goog.structs.Map<?, ?>|Object)} var_args
        1513 * The object from which key value pairs will be copied.
        1514 */
        1515goog.Uri.QueryData.prototype.extend = function(var_args) {
        1516 for (var i = 0; i < arguments.length; i++) {
        1517 var data = arguments[i];
        1518 goog.structs.forEach(data,
        1519 /** @this {goog.Uri.QueryData} */
        1520 function(value, key) {
        1521 this.add(key, value);
        1522 }, this);
        1523 }
        1524};
        \ No newline at end of file diff --git a/docs/source/lib/goog/uri/utils.js.src.html b/docs/source/lib/goog/uri/utils.js.src.html index 0313591..80dc8b1 100644 --- a/docs/source/lib/goog/uri/utils.js.src.html +++ b/docs/source/lib/goog/uri/utils.js.src.html @@ -1 +1 @@ -utils.js

        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
        48goog.provide('goog.uri.utils');
        49goog.provide('goog.uri.utils.ComponentIndex');
        50goog.provide('goog.uri.utils.QueryArray');
        51goog.provide('goog.uri.utils.QueryValue');
        52goog.provide('goog.uri.utils.StandardQueryParam');
        53
        54goog.require('goog.asserts');
        55goog.require('goog.string');
        56goog.require('goog.userAgent');
        57
        58
        59/**
        60 * Character codes inlined to avoid object allocations due to charCode.
        61 * @enum {number}
        62 * @private
        63 */
        64goog.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 */
        89goog.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 */
        189goog.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 */
        212goog.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 */
        238goog.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 */
        270goog.uri.utils.needsPhishingProtection_ = goog.userAgent.WEBKIT;
        271
        272
        273/**
        274 * Check to see if the user is being phished.
        275 * @private
        276 */
        277goog.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 */
        304goog.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 */
        321goog.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 */
        332goog.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 */
        344goog.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 */
        360goog.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 */
        370goog.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 */
        380goog.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 */
        390goog.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 */
        399goog.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 */
        414goog.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 */
        425goog.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 */
        435goog.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 */
        446goog.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 */
        459goog.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 */
        469goog.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 */
        480goog.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 */
        496goog.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 */
        510goog.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 */
        527goog.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 */
        545goog.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 */
        565goog.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 */
        589goog.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 */
        605goog.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 */
        640goog.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 */
        674goog.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 for 'a=1&b=2'.
        696 */
        697goog.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 */
        716goog.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 for 'a=1&b=2'.
        733 */
        734goog.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 */
        768goog.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 */
        785goog.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 */
        804goog.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 */
        830goog.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 */
        865goog.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 */
        878goog.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 */
        891goog.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 */
        920goog.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 */
        952goog.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 */
        961goog.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 */
        1002goog.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 */
        1018goog.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 * Standard supported query parameters.
        1035 * @enum {string}
        1036 */
        1037goog.uri.utils.StandardQueryParam = {
        1038
        1039 /** Unused parameter for unique-ifying. */
        1040 RANDOM: 'zx'
        1041};
        1042
        1043
        1044/**
        1045 * Sets the zx parameter of a URI to a random value.
        1046 * @param {string} uri Any URI.
        1047 * @return {string} That URI with the "zx" parameter added or replaced to
        1048 * contain a random string.
        1049 */
        1050goog.uri.utils.makeUnique = function(uri) {
        1051 return goog.uri.utils.setParam(uri,
        1052 goog.uri.utils.StandardQueryParam.RANDOM, goog.string.getRandomString());
        1053};
        \ No newline at end of file +utils.js

        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 operate on strings directly, rather than decoding them and
        39 * then 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 */
        46
        47goog.provide('goog.uri.utils');
        48goog.provide('goog.uri.utils.ComponentIndex');
        49goog.provide('goog.uri.utils.QueryArray');
        50goog.provide('goog.uri.utils.QueryValue');
        51goog.provide('goog.uri.utils.StandardQueryParam');
        52
        53goog.require('goog.asserts');
        54goog.require('goog.string');
        55
        56
        57/**
        58 * Character codes inlined to avoid object allocations due to charCode.
        59 * @enum {number}
        60 * @private
        61 */
        62goog.uri.utils.CharCode_ = {
        63 AMPERSAND: 38,
        64 EQUAL: 61,
        65 HASH: 35,
        66 QUESTION: 63
        67};
        68
        69
        70/**
        71 * Builds a URI string from already-encoded parts.
        72 *
        73 * No encoding is performed. Any component may be omitted as either null or
        74 * undefined.
        75 *
        76 * @param {?string=} opt_scheme The scheme such as 'http'.
        77 * @param {?string=} opt_userInfo The user name before the '@'.
        78 * @param {?string=} opt_domain The domain such as 'www.google.com', already
        79 * URI-encoded.
        80 * @param {(string|number|null)=} opt_port The port number.
        81 * @param {?string=} opt_path The path, already URI-encoded. If it is not
        82 * empty, it must begin with a slash.
        83 * @param {?string=} opt_queryData The URI-encoded query data.
        84 * @param {?string=} opt_fragment The URI-encoded fragment identifier.
        85 * @return {string} The fully combined URI.
        86 */
        87goog.uri.utils.buildFromEncodedParts = function(opt_scheme, opt_userInfo,
        88 opt_domain, opt_port, opt_path, opt_queryData, opt_fragment) {
        89 var out = '';
        90
        91 if (opt_scheme) {
        92 out += opt_scheme + ':';
        93 }
        94
        95 if (opt_domain) {
        96 out += '//';
        97
        98 if (opt_userInfo) {
        99 out += opt_userInfo + '@';
        100 }
        101
        102 out += opt_domain;
        103
        104 if (opt_port) {
        105 out += ':' + opt_port;
        106 }
        107 }
        108
        109 if (opt_path) {
        110 out += opt_path;
        111 }
        112
        113 if (opt_queryData) {
        114 out += '?' + opt_queryData;
        115 }
        116
        117 if (opt_fragment) {
        118 out += '#' + opt_fragment;
        119 }
        120
        121 return out;
        122};
        123
        124
        125/**
        126 * A regular expression for breaking a URI into its component parts.
        127 *
        128 * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B
        129 * As the "first-match-wins" algorithm is identical to the "greedy"
        130 * disambiguation method used by POSIX regular expressions, it is natural and
        131 * commonplace to use a regular expression for parsing the potential five
        132 * components of a URI reference.
        133 *
        134 * The following line is the regular expression for breaking-down a
        135 * well-formed URI reference into its components.
        136 *
        137 * <pre>
        138 * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
        139 * 12 3 4 5 6 7 8 9
        140 * </pre>
        141 *
        142 * The numbers in the second line above are only to assist readability; they
        143 * indicate the reference points for each subexpression (i.e., each paired
        144 * parenthesis). We refer to the value matched for subexpression <n> as $<n>.
        145 * For example, matching the above expression to
        146 * <pre>
        147 * http://www.ics.uci.edu/pub/ietf/uri/#Related
        148 * </pre>
        149 * results in the following subexpression matches:
        150 * <pre>
        151 * $1 = http:
        152 * $2 = http
        153 * $3 = //www.ics.uci.edu
        154 * $4 = www.ics.uci.edu
        155 * $5 = /pub/ietf/uri/
        156 * $6 = <undefined>
        157 * $7 = <undefined>
        158 * $8 = #Related
        159 * $9 = Related
        160 * </pre>
        161 * where <undefined> indicates that the component is not present, as is the
        162 * case for the query component in the above example. Therefore, we can
        163 * determine the value of the five components as
        164 * <pre>
        165 * scheme = $2
        166 * authority = $4
        167 * path = $5
        168 * query = $7
        169 * fragment = $9
        170 * </pre>
        171 *
        172 * The regular expression has been modified slightly to expose the
        173 * userInfo, domain, and port separately from the authority.
        174 * The modified version yields
        175 * <pre>
        176 * $1 = http scheme
        177 * $2 = <undefined> userInfo -\
        178 * $3 = www.ics.uci.edu domain | authority
        179 * $4 = <undefined> port -/
        180 * $5 = /pub/ietf/uri/ path
        181 * $6 = <undefined> query without ?
        182 * $7 = Related fragment without #
        183 * </pre>
        184 * @type {!RegExp}
        185 * @private
        186 */
        187goog.uri.utils.splitRe_ = new RegExp(
        188 '^' +
        189 '(?:' +
        190 '([^:/?#.]+)' + // scheme - ignore special characters
        191 // used by other URL parts such as :,
        192 // ?, /, #, and .
        193 ':)?' +
        194 '(?://' +
        195 '(?:([^/?#]*)@)?' + // userInfo
        196 '([^/#?]*?)' + // domain
        197 '(?::([0-9]+))?' + // port
        198 '(?=[/#?]|$)' + // authority-terminating character
        199 ')?' +
        200 '([^?#]+)?' + // path
        201 '(?:\\?([^#]*))?' + // query
        202 '(?:#(.*))?' + // fragment
        203 '$');
        204
        205
        206/**
        207 * The index of each URI component in the return value of goog.uri.utils.split.
        208 * @enum {number}
        209 */
        210goog.uri.utils.ComponentIndex = {
        211 SCHEME: 1,
        212 USER_INFO: 2,
        213 DOMAIN: 3,
        214 PORT: 4,
        215 PATH: 5,
        216 QUERY_DATA: 6,
        217 FRAGMENT: 7
        218};
        219
        220
        221/**
        222 * Splits a URI into its component parts.
        223 *
        224 * Each component can be accessed via the component indices; for example:
        225 * <pre>
        226 * goog.uri.utils.split(someStr)[goog.uri.utils.CompontentIndex.QUERY_DATA];
        227 * </pre>
        228 *
        229 * @param {string} uri The URI string to examine.
        230 * @return {!Array<string|undefined>} Each component still URI-encoded.
        231 * Each component that is present will contain the encoded value, whereas
        232 * components that are not present will be undefined or empty, depending
        233 * on the browser's regular expression implementation. Never null, since
        234 * arbitrary strings may still look like path names.
        235 */
        236goog.uri.utils.split = function(uri) {
        237 // See @return comment -- never null.
        238 return /** @type {!Array<string|undefined>} */ (
        239 uri.match(goog.uri.utils.splitRe_));
        240};
        241
        242
        243/**
        244 * @param {?string} uri A possibly null string.
        245 * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986
        246 * reserved characters will not be removed.
        247 * @return {?string} The string URI-decoded, or null if uri is null.
        248 * @private
        249 */
        250goog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) {
        251 if (!uri) {
        252 return uri;
        253 }
        254
        255 return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri);
        256};
        257
        258
        259/**
        260 * Gets a URI component by index.
        261 *
        262 * It is preferred to use the getPathEncoded() variety of functions ahead,
        263 * since they are more readable.
        264 *
        265 * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.
        266 * @param {string} uri The URI to examine.
        267 * @return {?string} The still-encoded component, or null if the component
        268 * is not present.
        269 * @private
        270 */
        271goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {
        272 // Convert undefined, null, and empty string into null.
        273 return goog.uri.utils.split(uri)[componentIndex] || null;
        274};
        275
        276
        277/**
        278 * @param {string} uri The URI to examine.
        279 * @return {?string} The protocol or scheme, or null if none. Does not
        280 * include trailing colons or slashes.
        281 */
        282goog.uri.utils.getScheme = function(uri) {
        283 return goog.uri.utils.getComponentByIndex_(
        284 goog.uri.utils.ComponentIndex.SCHEME, uri);
        285};
        286
        287
        288/**
        289 * Gets the effective scheme for the URL. If the URL is relative then the
        290 * scheme is derived from the page's location.
        291 * @param {string} uri The URI to examine.
        292 * @return {string} The protocol or scheme, always lower case.
        293 */
        294goog.uri.utils.getEffectiveScheme = function(uri) {
        295 var scheme = goog.uri.utils.getScheme(uri);
        296 if (!scheme && goog.global.self && goog.global.self.location) {
        297 var protocol = goog.global.self.location.protocol;
        298 scheme = protocol.substr(0, protocol.length - 1);
        299 }
        300 // NOTE: When called from a web worker in Firefox 3.5, location maybe null.
        301 // All other browsers with web workers support self.location from the worker.
        302 return scheme ? scheme.toLowerCase() : '';
        303};
        304
        305
        306/**
        307 * @param {string} uri The URI to examine.
        308 * @return {?string} The user name still encoded, or null if none.
        309 */
        310goog.uri.utils.getUserInfoEncoded = function(uri) {
        311 return goog.uri.utils.getComponentByIndex_(
        312 goog.uri.utils.ComponentIndex.USER_INFO, uri);
        313};
        314
        315
        316/**
        317 * @param {string} uri The URI to examine.
        318 * @return {?string} The decoded user info, or null if none.
        319 */
        320goog.uri.utils.getUserInfo = function(uri) {
        321 return goog.uri.utils.decodeIfPossible_(
        322 goog.uri.utils.getUserInfoEncoded(uri));
        323};
        324
        325
        326/**
        327 * @param {string} uri The URI to examine.
        328 * @return {?string} The domain name still encoded, or null if none.
        329 */
        330goog.uri.utils.getDomainEncoded = function(uri) {
        331 return goog.uri.utils.getComponentByIndex_(
        332 goog.uri.utils.ComponentIndex.DOMAIN, uri);
        333};
        334
        335
        336/**
        337 * @param {string} uri The URI to examine.
        338 * @return {?string} The decoded domain, or null if none.
        339 */
        340goog.uri.utils.getDomain = function(uri) {
        341 return goog.uri.utils.decodeIfPossible_(
        342 goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */);
        343};
        344
        345
        346/**
        347 * @param {string} uri The URI to examine.
        348 * @return {?number} The port number, or null if none.
        349 */
        350goog.uri.utils.getPort = function(uri) {
        351 // Coerce to a number. If the result of getComponentByIndex_ is null or
        352 // non-numeric, the number coersion yields NaN. This will then return
        353 // null for all non-numeric cases (though also zero, which isn't a relevant
        354 // port number).
        355 return Number(goog.uri.utils.getComponentByIndex_(
        356 goog.uri.utils.ComponentIndex.PORT, uri)) || null;
        357};
        358
        359
        360/**
        361 * @param {string} uri The URI to examine.
        362 * @return {?string} The path still encoded, or null if none. Includes the
        363 * leading slash, if any.
        364 */
        365goog.uri.utils.getPathEncoded = function(uri) {
        366 return goog.uri.utils.getComponentByIndex_(
        367 goog.uri.utils.ComponentIndex.PATH, uri);
        368};
        369
        370
        371/**
        372 * @param {string} uri The URI to examine.
        373 * @return {?string} The decoded path, or null if none. Includes the leading
        374 * slash, if any.
        375 */
        376goog.uri.utils.getPath = function(uri) {
        377 return goog.uri.utils.decodeIfPossible_(
        378 goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */);
        379};
        380
        381
        382/**
        383 * @param {string} uri The URI to examine.
        384 * @return {?string} The query data still encoded, or null if none. Does not
        385 * include the question mark itself.
        386 */
        387goog.uri.utils.getQueryData = function(uri) {
        388 return goog.uri.utils.getComponentByIndex_(
        389 goog.uri.utils.ComponentIndex.QUERY_DATA, uri);
        390};
        391
        392
        393/**
        394 * @param {string} uri The URI to examine.
        395 * @return {?string} The fragment identifier, or null if none. Does not
        396 * include the hash mark itself.
        397 */
        398goog.uri.utils.getFragmentEncoded = function(uri) {
        399 // The hash mark may not appear in any other part of the URL.
        400 var hashIndex = uri.indexOf('#');
        401 return hashIndex < 0 ? null : uri.substr(hashIndex + 1);
        402};
        403
        404
        405/**
        406 * @param {string} uri The URI to examine.
        407 * @param {?string} fragment The encoded fragment identifier, or null if none.
        408 * Does not include the hash mark itself.
        409 * @return {string} The URI with the fragment set.
        410 */
        411goog.uri.utils.setFragmentEncoded = function(uri, fragment) {
        412 return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');
        413};
        414
        415
        416/**
        417 * @param {string} uri The URI to examine.
        418 * @return {?string} The decoded fragment identifier, or null if none. Does
        419 * not include the hash mark.
        420 */
        421goog.uri.utils.getFragment = function(uri) {
        422 return goog.uri.utils.decodeIfPossible_(
        423 goog.uri.utils.getFragmentEncoded(uri));
        424};
        425
        426
        427/**
        428 * Extracts everything up to the port of the URI.
        429 * @param {string} uri The URI string.
        430 * @return {string} Everything up to and including the port.
        431 */
        432goog.uri.utils.getHost = function(uri) {
        433 var pieces = goog.uri.utils.split(uri);
        434 return goog.uri.utils.buildFromEncodedParts(
        435 pieces[goog.uri.utils.ComponentIndex.SCHEME],
        436 pieces[goog.uri.utils.ComponentIndex.USER_INFO],
        437 pieces[goog.uri.utils.ComponentIndex.DOMAIN],
        438 pieces[goog.uri.utils.ComponentIndex.PORT]);
        439};
        440
        441
        442/**
        443 * Extracts the path of the URL and everything after.
        444 * @param {string} uri The URI string.
        445 * @return {string} The URI, starting at the path and including the query
        446 * parameters and fragment identifier.
        447 */
        448goog.uri.utils.getPathAndAfter = function(uri) {
        449 var pieces = goog.uri.utils.split(uri);
        450 return goog.uri.utils.buildFromEncodedParts(null, null, null, null,
        451 pieces[goog.uri.utils.ComponentIndex.PATH],
        452 pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],
        453 pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);
        454};
        455
        456
        457/**
        458 * Gets the URI with the fragment identifier removed.
        459 * @param {string} uri The URI to examine.
        460 * @return {string} Everything preceding the hash mark.
        461 */
        462goog.uri.utils.removeFragment = function(uri) {
        463 // The hash mark may not appear in any other part of the URL.
        464 var hashIndex = uri.indexOf('#');
        465 return hashIndex < 0 ? uri : uri.substr(0, hashIndex);
        466};
        467
        468
        469/**
        470 * Ensures that two URI's have the exact same domain, scheme, and port.
        471 *
        472 * Unlike the version in goog.Uri, this checks protocol, and therefore is
        473 * suitable for checking against the browser's same-origin policy.
        474 *
        475 * @param {string} uri1 The first URI.
        476 * @param {string} uri2 The second URI.
        477 * @return {boolean} Whether they have the same scheme, domain and port.
        478 */
        479goog.uri.utils.haveSameDomain = function(uri1, uri2) {
        480 var pieces1 = goog.uri.utils.split(uri1);
        481 var pieces2 = goog.uri.utils.split(uri2);
        482 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
        483 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
        484 pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==
        485 pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&
        486 pieces1[goog.uri.utils.ComponentIndex.PORT] ==
        487 pieces2[goog.uri.utils.ComponentIndex.PORT];
        488};
        489
        490
        491/**
        492 * Asserts that there are no fragment or query identifiers, only in uncompiled
        493 * mode.
        494 * @param {string} uri The URI to examine.
        495 * @private
        496 */
        497goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {
        498 // NOTE: would use goog.asserts here, but jscompiler doesn't know that
        499 // indexOf has no side effects.
        500 if (goog.DEBUG && (uri.indexOf('#') >= 0 || uri.indexOf('?') >= 0)) {
        501 throw Error('goog.uri.utils: Fragment or query identifiers are not ' +
        502 'supported: [' + uri + ']');
        503 }
        504};
        505
        506
        507/**
        508 * Supported query parameter values by the parameter serializing utilities.
        509 *
        510 * If a value is null or undefined, the key-value pair is skipped, as an easy
        511 * way to omit parameters conditionally. Non-array parameters are converted
        512 * to a string and URI encoded. Array values are expanded into multiple
        513 * &key=value pairs, with each element stringized and URI-encoded.
        514 *
        515 * @typedef {*}
        516 */
        517goog.uri.utils.QueryValue;
        518
        519
        520/**
        521 * An array representing a set of query parameters with alternating keys
        522 * and values.
        523 *
        524 * Keys are assumed to be URI encoded already and live at even indices. See
        525 * goog.uri.utils.QueryValue for details on how parameter values are encoded.
        526 *
        527 * Example:
        528 * <pre>
        529 * var data = [
        530 * // Simple param: ?name=BobBarker
        531 * 'name', 'BobBarker',
        532 * // Conditional param -- may be omitted entirely.
        533 * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,
        534 * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null
        535 * 'house', ['LosAngeles', 'NewYork', null]
        536 * ];
        537 * </pre>
        538 *
        539 * @typedef {!Array<string|goog.uri.utils.QueryValue>}
        540 */
        541goog.uri.utils.QueryArray;
        542
        543
        544/**
        545 * Parses encoded query parameters and calls callback function for every
        546 * parameter found in the string.
        547 *
        548 * Missing value of parameter (e.g. “…&key&…”) is treated as if the value was an
        549 * empty string. Keys may be empty strings (e.g. “…&=value&…”) which also means
        550 * that “…&=&…” and “…&&…” will result in an empty key and value.
        551 *
        552 * @param {string} encodedQuery Encoded query string excluding question mark at
        553 * the beginning.
        554 * @param {function(string, string)} callback Function called for every
        555 * parameter found in query string. The first argument (name) will not be
        556 * urldecoded (so the function is consistent with buildQueryData), but the
        557 * second will. If the parameter has no value (i.e. “=” was not present)
        558 * the second argument (value) will be an empty string.
        559 */
        560goog.uri.utils.parseQueryData = function(encodedQuery, callback) {
        561 if (!encodedQuery) {
        562 return;
        563 }
        564 var pairs = encodedQuery.split('&');
        565 for (var i = 0; i < pairs.length; i++) {
        566 var indexOfEquals = pairs[i].indexOf('=');
        567 var name = null;
        568 var value = null;
        569 if (indexOfEquals >= 0) {
        570 name = pairs[i].substring(0, indexOfEquals);
        571 value = pairs[i].substring(indexOfEquals + 1);
        572 } else {
        573 name = pairs[i];
        574 }
        575 callback(name, value ? goog.string.urlDecode(value) : '');
        576 }
        577};
        578
        579
        580/**
        581 * Appends a URI and query data in a string buffer with special preconditions.
        582 *
        583 * Internal implementation utility, performing very few object allocations.
        584 *
        585 * @param {!Array<string|undefined>} buffer A string buffer. The first element
        586 * must be the base URI, and may have a fragment identifier. If the array
        587 * contains more than one element, the second element must be an ampersand,
        588 * and may be overwritten, depending on the base URI. Undefined elements
        589 * are treated as empty-string.
        590 * @return {string} The concatenated URI and query data.
        591 * @private
        592 */
        593goog.uri.utils.appendQueryData_ = function(buffer) {
        594 if (buffer[1]) {
        595 // At least one query parameter was added. We need to check the
        596 // punctuation mark, which is currently an ampersand, and also make sure
        597 // there aren't any interfering fragment identifiers.
        598 var baseUri = /** @type {string} */ (buffer[0]);
        599 var hashIndex = baseUri.indexOf('#');
        600 if (hashIndex >= 0) {
        601 // Move the fragment off the base part of the URI into the end.
        602 buffer.push(baseUri.substr(hashIndex));
        603 buffer[0] = baseUri = baseUri.substr(0, hashIndex);
        604 }
        605 var questionIndex = baseUri.indexOf('?');
        606 if (questionIndex < 0) {
        607 // No question mark, so we need a question mark instead of an ampersand.
        608 buffer[1] = '?';
        609 } else if (questionIndex == baseUri.length - 1) {
        610 // Question mark is the very last character of the existing URI, so don't
        611 // append an additional delimiter.
        612 buffer[1] = undefined;
        613 }
        614 }
        615
        616 return buffer.join('');
        617};
        618
        619
        620/**
        621 * Appends key=value pairs to an array, supporting multi-valued objects.
        622 * @param {string} key The key prefix.
        623 * @param {goog.uri.utils.QueryValue} value The value to serialize.
        624 * @param {!Array<string>} pairs The array to which the 'key=value' strings
        625 * should be appended.
        626 * @private
        627 */
        628goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {
        629 if (goog.isArray(value)) {
        630 // Convince the compiler it's an array.
        631 goog.asserts.assertArray(value);
        632 for (var j = 0; j < value.length; j++) {
        633 // Convert to string explicitly, to short circuit the null and array
        634 // logic in this function -- this ensures that null and undefined get
        635 // written as literal 'null' and 'undefined', and arrays don't get
        636 // expanded out but instead encoded in the default way.
        637 goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);
        638 }
        639 } else if (value != null) {
        640 // Skip a top-level null or undefined entirely.
        641 pairs.push('&', key,
        642 // Check for empty string. Zero gets encoded into the url as literal
        643 // strings. For empty string, skip the equal sign, to be consistent
        644 // with UriBuilder.java.
        645 value === '' ? '' : '=',
        646 goog.string.urlEncode(value));
        647 }
        648};
        649
        650
        651/**
        652 * Builds a buffer of query data from a sequence of alternating keys and values.
        653 *
        654 * @param {!Array<string|undefined>} buffer A string buffer to append to. The
        655 * first element appended will be an '&', and may be replaced by the caller.
        656 * @param {!goog.uri.utils.QueryArray|!Arguments} keysAndValues An array with
        657 * alternating keys and values -- see the typedef.
        658 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
        659 * @return {!Array<string|undefined>} The buffer argument.
        660 * @private
        661 */
        662goog.uri.utils.buildQueryDataBuffer_ = function(
        663 buffer, keysAndValues, opt_startIndex) {
        664 goog.asserts.assert(Math.max(keysAndValues.length - (opt_startIndex || 0),
        665 0) % 2 == 0, 'goog.uri.utils: Key/value lists must be even in length.');
        666
        667 for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {
        668 goog.uri.utils.appendKeyValuePairs_(
        669 keysAndValues[i], keysAndValues[i + 1], buffer);
        670 }
        671
        672 return buffer;
        673};
        674
        675
        676/**
        677 * Builds a query data string from a sequence of alternating keys and values.
        678 * Currently generates "&key&" for empty args.
        679 *
        680 * @param {goog.uri.utils.QueryArray} keysAndValues Alternating keys and
        681 * values. See the typedef.
        682 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
        683 * @return {string} The encoded query string, in the form 'a=1&b=2'.
        684 */
        685goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {
        686 var buffer = goog.uri.utils.buildQueryDataBuffer_(
        687 [], keysAndValues, opt_startIndex);
        688 buffer[0] = ''; // Remove the leading ampersand.
        689 return buffer.join('');
        690};
        691
        692
        693/**
        694 * Builds a buffer of query data from a map.
        695 *
        696 * @param {!Array<string|undefined>} buffer A string buffer to append to. The
        697 * first element appended will be an '&', and may be replaced by the caller.
        698 * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys
        699 * are URI-encoded parameter keys, and the values conform to the contract
        700 * specified in the goog.uri.utils.QueryValue typedef.
        701 * @return {!Array<string|undefined>} The buffer argument.
        702 * @private
        703 */
        704goog.uri.utils.buildQueryDataBufferFromMap_ = function(buffer, map) {
        705 for (var key in map) {
        706 goog.uri.utils.appendKeyValuePairs_(key, map[key], buffer);
        707 }
        708
        709 return buffer;
        710};
        711
        712
        713/**
        714 * Builds a query data string from a map.
        715 * Currently generates "&key&" for empty args.
        716 *
        717 * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys
        718 * are URI-encoded parameter keys, and the values are arbitrary types
        719 * or arrays. Keys with a null value are dropped.
        720 * @return {string} The encoded query string, in the form 'a=1&b=2'.
        721 */
        722goog.uri.utils.buildQueryDataFromMap = function(map) {
        723 var buffer = goog.uri.utils.buildQueryDataBufferFromMap_([], map);
        724 buffer[0] = '';
        725 return buffer.join('');
        726};
        727
        728
        729/**
        730 * Appends URI parameters to an existing URI.
        731 *
        732 * The variable arguments may contain alternating keys and values. Keys are
        733 * assumed to be already URI encoded. The values should not be URI-encoded,
        734 * and will instead be encoded by this function.
        735 * <pre>
        736 * appendParams('http://www.foo.com?existing=true',
        737 * 'key1', 'value1',
        738 * 'key2', 'value?willBeEncoded',
        739 * 'key3', ['valueA', 'valueB', 'valueC'],
        740 * 'key4', null);
        741 * result: 'http://www.foo.com?existing=true&' +
        742 * 'key1=value1&' +
        743 * 'key2=value%3FwillBeEncoded&' +
        744 * 'key3=valueA&key3=valueB&key3=valueC'
        745 * </pre>
        746 *
        747 * A single call to this function will not exhibit quadratic behavior in IE,
        748 * whereas multiple repeated calls may, although the effect is limited by
        749 * fact that URL's generally can't exceed 2kb.
        750 *
        751 * @param {string} uri The original URI, which may already have query data.
        752 * @param {...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)} var_args
        753 * An array or argument list conforming to goog.uri.utils.QueryArray.
        754 * @return {string} The URI with all query parameters added.
        755 */
        756goog.uri.utils.appendParams = function(uri, var_args) {
        757 return goog.uri.utils.appendQueryData_(
        758 arguments.length == 2 ?
        759 goog.uri.utils.buildQueryDataBuffer_([uri], arguments[1], 0) :
        760 goog.uri.utils.buildQueryDataBuffer_([uri], arguments, 1));
        761};
        762
        763
        764/**
        765 * Appends query parameters from a map.
        766 *
        767 * @param {string} uri The original URI, which may already have query data.
        768 * @param {!Object<goog.uri.utils.QueryValue>} map An object where keys are
        769 * URI-encoded parameter keys, and the values are arbitrary types or arrays.
        770 * Keys with a null value are dropped.
        771 * @return {string} The new parameters.
        772 */
        773goog.uri.utils.appendParamsFromMap = function(uri, map) {
        774 return goog.uri.utils.appendQueryData_(
        775 goog.uri.utils.buildQueryDataBufferFromMap_([uri], map));
        776};
        777
        778
        779/**
        780 * Appends a single URI parameter.
        781 *
        782 * Repeated calls to this can exhibit quadratic behavior in IE6 due to the
        783 * way string append works, though it should be limited given the 2kb limit.
        784 *
        785 * @param {string} uri The original URI, which may already have query data.
        786 * @param {string} key The key, which must already be URI encoded.
        787 * @param {*=} opt_value The value, which will be stringized and encoded
        788 * (assumed not already to be encoded). If omitted, undefined, or null, the
        789 * key will be added as a valueless parameter.
        790 * @return {string} The URI with the query parameter added.
        791 */
        792goog.uri.utils.appendParam = function(uri, key, opt_value) {
        793 var paramArr = [uri, '&', key];
        794 if (goog.isDefAndNotNull(opt_value)) {
        795 paramArr.push('=', goog.string.urlEncode(opt_value));
        796 }
        797 return goog.uri.utils.appendQueryData_(paramArr);
        798};
        799
        800
        801/**
        802 * Finds the next instance of a query parameter with the specified name.
        803 *
        804 * Does not instantiate any objects.
        805 *
        806 * @param {string} uri The URI to search. May contain a fragment identifier
        807 * if opt_hashIndex is specified.
        808 * @param {number} startIndex The index to begin searching for the key at. A
        809 * match may be found even if this is one character after the ampersand.
        810 * @param {string} keyEncoded The URI-encoded key.
        811 * @param {number} hashOrEndIndex Index to stop looking at. If a hash
        812 * mark is present, it should be its index, otherwise it should be the
        813 * length of the string.
        814 * @return {number} The position of the first character in the key's name,
        815 * immediately after either a question mark or a dot.
        816 * @private
        817 */
        818goog.uri.utils.findParam_ = function(
        819 uri, startIndex, keyEncoded, hashOrEndIndex) {
        820 var index = startIndex;
        821 var keyLength = keyEncoded.length;
        822
        823 // Search for the key itself and post-filter for surronuding punctuation,
        824 // rather than expensively building a regexp.
        825 while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&
        826 index < hashOrEndIndex) {
        827 var precedingChar = uri.charCodeAt(index - 1);
        828 // Ensure that the preceding character is '&' or '?'.
        829 if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||
        830 precedingChar == goog.uri.utils.CharCode_.QUESTION) {
        831 // Ensure the following character is '&', '=', '#', or NaN
        832 // (end of string).
        833 var followingChar = uri.charCodeAt(index + keyLength);
        834 if (!followingChar ||
        835 followingChar == goog.uri.utils.CharCode_.EQUAL ||
        836 followingChar == goog.uri.utils.CharCode_.AMPERSAND ||
        837 followingChar == goog.uri.utils.CharCode_.HASH) {
        838 return index;
        839 }
        840 }
        841 index += keyLength + 1;
        842 }
        843
        844 return -1;
        845};
        846
        847
        848/**
        849 * Regular expression for finding a hash mark or end of string.
        850 * @type {RegExp}
        851 * @private
        852 */
        853goog.uri.utils.hashOrEndRe_ = /#|$/;
        854
        855
        856/**
        857 * Determines if the URI contains a specific key.
        858 *
        859 * Performs no object instantiations.
        860 *
        861 * @param {string} uri The URI to process. May contain a fragment
        862 * identifier.
        863 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
        864 * @return {boolean} Whether the key is present.
        865 */
        866goog.uri.utils.hasParam = function(uri, keyEncoded) {
        867 return goog.uri.utils.findParam_(uri, 0, keyEncoded,
        868 uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;
        869};
        870
        871
        872/**
        873 * Gets the first value of a query parameter.
        874 * @param {string} uri The URI to process. May contain a fragment.
        875 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
        876 * @return {?string} The first value of the parameter (URI-decoded), or null
        877 * if the parameter is not found.
        878 */
        879goog.uri.utils.getParamValue = function(uri, keyEncoded) {
        880 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
        881 var foundIndex = goog.uri.utils.findParam_(
        882 uri, 0, keyEncoded, hashOrEndIndex);
        883
        884 if (foundIndex < 0) {
        885 return null;
        886 } else {
        887 var endPosition = uri.indexOf('&', foundIndex);
        888 if (endPosition < 0 || endPosition > hashOrEndIndex) {
        889 endPosition = hashOrEndIndex;
        890 }
        891 // Progress forth to the end of the "key=" or "key&" substring.
        892 foundIndex += keyEncoded.length + 1;
        893 // Use substr, because it (unlike substring) will return empty string
        894 // if foundIndex > endPosition.
        895 return goog.string.urlDecode(
        896 uri.substr(foundIndex, endPosition - foundIndex));
        897 }
        898};
        899
        900
        901/**
        902 * Gets all values of a query parameter.
        903 * @param {string} uri The URI to process. May contain a fragment.
        904 * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
        905 * @return {!Array<string>} All URI-decoded values with the given key.
        906 * If the key is not found, this will have length 0, but never be null.
        907 */
        908goog.uri.utils.getParamValues = function(uri, keyEncoded) {
        909 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
        910 var position = 0;
        911 var foundIndex;
        912 var result = [];
        913
        914 while ((foundIndex = goog.uri.utils.findParam_(
        915 uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
        916 // Find where this parameter ends, either the '&' or the end of the
        917 // query parameters.
        918 position = uri.indexOf('&', foundIndex);
        919 if (position < 0 || position > hashOrEndIndex) {
        920 position = hashOrEndIndex;
        921 }
        922
        923 // Progress forth to the end of the "key=" or "key&" substring.
        924 foundIndex += keyEncoded.length + 1;
        925 // Use substr, because it (unlike substring) will return empty string
        926 // if foundIndex > position.
        927 result.push(goog.string.urlDecode(uri.substr(
        928 foundIndex, position - foundIndex)));
        929 }
        930
        931 return result;
        932};
        933
        934
        935/**
        936 * Regexp to find trailing question marks and ampersands.
        937 * @type {RegExp}
        938 * @private
        939 */
        940goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;
        941
        942
        943/**
        944 * Removes all instances of a query parameter.
        945 * @param {string} uri The URI to process. Must not contain a fragment.
        946 * @param {string} keyEncoded The URI-encoded key.
        947 * @return {string} The URI with all instances of the parameter removed.
        948 */
        949goog.uri.utils.removeParam = function(uri, keyEncoded) {
        950 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
        951 var position = 0;
        952 var foundIndex;
        953 var buffer = [];
        954
        955 // Look for a query parameter.
        956 while ((foundIndex = goog.uri.utils.findParam_(
        957 uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
        958 // Get the portion of the query string up to, but not including, the ?
        959 // or & starting the parameter.
        960 buffer.push(uri.substring(position, foundIndex));
        961 // Progress to immediately after the '&'. If not found, go to the end.
        962 // Avoid including the hash mark.
        963 position = Math.min((uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex,
        964 hashOrEndIndex);
        965 }
        966
        967 // Append everything that is remaining.
        968 buffer.push(uri.substr(position));
        969
        970 // Join the buffer, and remove trailing punctuation that remains.
        971 return buffer.join('').replace(
        972 goog.uri.utils.trailingQueryPunctuationRe_, '$1');
        973};
        974
        975
        976/**
        977 * Replaces all existing definitions of a parameter with a single definition.
        978 *
        979 * Repeated calls to this can exhibit quadratic behavior due to the need to
        980 * find existing instances and reconstruct the string, though it should be
        981 * limited given the 2kb limit. Consider using appendParams to append multiple
        982 * parameters in bulk.
        983 *
        984 * @param {string} uri The original URI, which may already have query data.
        985 * @param {string} keyEncoded The key, which must already be URI encoded.
        986 * @param {*} value The value, which will be stringized and encoded (assumed
        987 * not already to be encoded).
        988 * @return {string} The URI with the query parameter added.
        989 */
        990goog.uri.utils.setParam = function(uri, keyEncoded, value) {
        991 return goog.uri.utils.appendParam(
        992 goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);
        993};
        994
        995
        996/**
        997 * Generates a URI path using a given URI and a path with checks to
        998 * prevent consecutive "//". The baseUri passed in must not contain
        999 * query or fragment identifiers. The path to append may not contain query or
        1000 * fragment identifiers.
        1001 *
        1002 * @param {string} baseUri URI to use as the base.
        1003 * @param {string} path Path to append.
        1004 * @return {string} Updated URI.
        1005 */
        1006goog.uri.utils.appendPath = function(baseUri, path) {
        1007 goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);
        1008
        1009 // Remove any trailing '/'
        1010 if (goog.string.endsWith(baseUri, '/')) {
        1011 baseUri = baseUri.substr(0, baseUri.length - 1);
        1012 }
        1013 // Remove any leading '/'
        1014 if (goog.string.startsWith(path, '/')) {
        1015 path = path.substr(1);
        1016 }
        1017 return goog.string.buildString(baseUri, '/', path);
        1018};
        1019
        1020
        1021/**
        1022 * Replaces the path.
        1023 * @param {string} uri URI to use as the base.
        1024 * @param {string} path New path.
        1025 * @return {string} Updated URI.
        1026 */
        1027goog.uri.utils.setPath = function(uri, path) {
        1028 // Add any missing '/'.
        1029 if (!goog.string.startsWith(path, '/')) {
        1030 path = '/' + path;
        1031 }
        1032 var parts = goog.uri.utils.split(uri);
        1033 return goog.uri.utils.buildFromEncodedParts(
        1034 parts[goog.uri.utils.ComponentIndex.SCHEME],
        1035 parts[goog.uri.utils.ComponentIndex.USER_INFO],
        1036 parts[goog.uri.utils.ComponentIndex.DOMAIN],
        1037 parts[goog.uri.utils.ComponentIndex.PORT],
        1038 path,
        1039 parts[goog.uri.utils.ComponentIndex.QUERY_DATA],
        1040 parts[goog.uri.utils.ComponentIndex.FRAGMENT]);
        1041};
        1042
        1043
        1044/**
        1045 * Standard supported query parameters.
        1046 * @enum {string}
        1047 */
        1048goog.uri.utils.StandardQueryParam = {
        1049
        1050 /** Unused parameter for unique-ifying. */
        1051 RANDOM: 'zx'
        1052};
        1053
        1054
        1055/**
        1056 * Sets the zx parameter of a URI to a random value.
        1057 * @param {string} uri Any URI.
        1058 * @return {string} That URI with the "zx" parameter added or replaced to
        1059 * contain a random string.
        1060 */
        1061goog.uri.utils.makeUnique = function(uri) {
        1062 return goog.uri.utils.setParam(uri,
        1063 goog.uri.utils.StandardQueryParam.RANDOM, goog.string.getRandomString());
        1064};
        \ No newline at end of file diff --git a/docs/source/lib/goog/useragent/product.js.src.html b/docs/source/lib/goog/useragent/product.js.src.html index 1ff8b1c..244d4d3 100644 --- a/docs/source/lib/goog/useragent/product.js.src.html +++ b/docs/source/lib/goog/useragent/product.js.src.html @@ -1 +1 @@ -product.js

        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
        20goog.provide('goog.userAgent.product');
        21
        22goog.require('goog.userAgent');
        23
        24
        25/**
        26 * @define {boolean} Whether the code is running on the Firefox web browser.
        27 */
        28goog.define('goog.userAgent.product.ASSUME_FIREFOX', false);
        29
        30
        31/**
        32 * @define {boolean} Whether the code is running on the Camino web browser.
        33 */
        34goog.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 */
        41goog.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 */
        48goog.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 */
        55goog.define('goog.userAgent.product.ASSUME_ANDROID', false);
        56
        57
        58/**
        59 * @define {boolean} Whether the code is running on the Chrome web browser.
        60 */
        61goog.define('goog.userAgent.product.ASSUME_CHROME', false);
        62
        63
        64/**
        65 * @define {boolean} Whether the code is running on the Safari web browser.
        66 */
        67goog.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 */
        75goog.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 */
        95goog.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('Android') != -1) {
        166 goog.userAgent.product.detectedAndroid_ = true;
        167 } else if (ua.indexOf('Chrome') != -1) {
        168 goog.userAgent.product.detectedChrome_ = true;
        169 } else if (ua.indexOf('Safari') != -1) {
        170 goog.userAgent.product.detectedSafari_ = true;
        171 }
        172};
        173
        174if (!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 */
        183goog.userAgent.product.OPERA = goog.userAgent.OPERA;
        184
        185
        186/**
        187 * Whether the code is running on an IE web browser.
        188 * @type {boolean}
        189 */
        190goog.userAgent.product.IE = goog.userAgent.IE;
        191
        192
        193/**
        194 * Whether the code is running on the Firefox web browser.
        195 * @type {boolean}
        196 */
        197goog.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 */
        206goog.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 */
        215goog.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 */
        224goog.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 */
        233goog.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 */
        242goog.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 */
        251goog.userAgent.product.SAFARI = goog.userAgent.product.PRODUCT_KNOWN_ ?
        252 goog.userAgent.product.ASSUME_SAFARI :
        253 goog.userAgent.product.detectedSafari_;
        \ No newline at end of file +product.js

        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
        20goog.provide('goog.userAgent.product');
        21
        22goog.require('goog.labs.userAgent.browser');
        23goog.require('goog.labs.userAgent.platform');
        24goog.require('goog.userAgent');
        25
        26
        27/**
        28 * @define {boolean} Whether the code is running on the Firefox web browser.
        29 */
        30goog.define('goog.userAgent.product.ASSUME_FIREFOX', false);
        31
        32
        33/**
        34 * @define {boolean} Whether we know at compile-time that the product is an
        35 * iPhone.
        36 */
        37goog.define('goog.userAgent.product.ASSUME_IPHONE', false);
        38
        39
        40/**
        41 * @define {boolean} Whether we know at compile-time that the product is an
        42 * iPad.
        43 */
        44goog.define('goog.userAgent.product.ASSUME_IPAD', false);
        45
        46
        47/**
        48 * @define {boolean} Whether we know at compile-time that the product is an
        49 * AOSP browser or WebView inside a pre KitKat Android phone or tablet.
        50 */
        51goog.define('goog.userAgent.product.ASSUME_ANDROID', false);
        52
        53
        54/**
        55 * @define {boolean} Whether the code is running on the Chrome web browser on
        56 * any platform or AOSP browser or WebView in a KitKat+ Android phone or tablet.
        57 */
        58goog.define('goog.userAgent.product.ASSUME_CHROME', false);
        59
        60
        61/**
        62 * @define {boolean} Whether the code is running on the Safari web browser.
        63 */
        64goog.define('goog.userAgent.product.ASSUME_SAFARI', false);
        65
        66
        67/**
        68 * Whether we know the product type at compile-time.
        69 * @type {boolean}
        70 * @private
        71 */
        72goog.userAgent.product.PRODUCT_KNOWN_ =
        73 goog.userAgent.ASSUME_IE ||
        74 goog.userAgent.ASSUME_EDGE ||
        75 goog.userAgent.ASSUME_OPERA ||
        76 goog.userAgent.product.ASSUME_FIREFOX ||
        77 goog.userAgent.product.ASSUME_IPHONE ||
        78 goog.userAgent.product.ASSUME_IPAD ||
        79 goog.userAgent.product.ASSUME_ANDROID ||
        80 goog.userAgent.product.ASSUME_CHROME ||
        81 goog.userAgent.product.ASSUME_SAFARI;
        82
        83
        84/**
        85 * Whether the code is running on the Opera web browser.
        86 * @type {boolean}
        87 */
        88goog.userAgent.product.OPERA = goog.userAgent.OPERA;
        89
        90
        91/**
        92 * Whether the code is running on an IE web browser.
        93 * @type {boolean}
        94 */
        95goog.userAgent.product.IE = goog.userAgent.IE;
        96
        97
        98/**
        99 * Whether the code is running on an Edge web browser.
        100 * @type {boolean}
        101 */
        102goog.userAgent.product.EDGE = goog.userAgent.EDGE;
        103
        104
        105/**
        106 * Whether the code is running on the Firefox web browser.
        107 * @type {boolean}
        108 */
        109goog.userAgent.product.FIREFOX = goog.userAgent.product.PRODUCT_KNOWN_ ?
        110 goog.userAgent.product.ASSUME_FIREFOX :
        111 goog.labs.userAgent.browser.isFirefox();
        112
        113
        114/**
        115 * Whether the user agent is an iPhone or iPod (as in iPod touch).
        116 * @return {boolean}
        117 * @private
        118 */
        119goog.userAgent.product.isIphoneOrIpod_ = function() {
        120 return goog.labs.userAgent.platform.isIphone() ||
        121 goog.labs.userAgent.platform.isIpod();
        122};
        123
        124
        125/**
        126 * Whether the code is running on an iPhone or iPod touch.
        127 *
        128 * iPod touch is considered an iPhone for legacy reasons.
        129 * @type {boolean}
        130 */
        131goog.userAgent.product.IPHONE = goog.userAgent.product.PRODUCT_KNOWN_ ?
        132 goog.userAgent.product.ASSUME_IPHONE :
        133 goog.userAgent.product.isIphoneOrIpod_();
        134
        135
        136/**
        137 * Whether the code is running on an iPad.
        138 * @type {boolean}
        139 */
        140goog.userAgent.product.IPAD = goog.userAgent.product.PRODUCT_KNOWN_ ?
        141 goog.userAgent.product.ASSUME_IPAD :
        142 goog.labs.userAgent.platform.isIpad();
        143
        144
        145/**
        146 * Whether the code is running on AOSP browser or WebView inside
        147 * a pre KitKat Android phone or tablet.
        148 * @type {boolean}
        149 */
        150goog.userAgent.product.ANDROID = goog.userAgent.product.PRODUCT_KNOWN_ ?
        151 goog.userAgent.product.ASSUME_ANDROID :
        152 goog.labs.userAgent.browser.isAndroidBrowser();
        153
        154
        155/**
        156 * Whether the code is running on the Chrome web browser on any platform
        157 * or AOSP browser or WebView in a KitKat+ Android phone or tablet.
        158 * @type {boolean}
        159 */
        160goog.userAgent.product.CHROME = goog.userAgent.product.PRODUCT_KNOWN_ ?
        161 goog.userAgent.product.ASSUME_CHROME :
        162 goog.labs.userAgent.browser.isChrome();
        163
        164
        165/**
        166 * @return {boolean} Whether the browser is Safari on desktop.
        167 * @private
        168 */
        169goog.userAgent.product.isSafariDesktop_ = function() {
        170 return goog.labs.userAgent.browser.isSafari() &&
        171 !goog.labs.userAgent.platform.isIos();
        172};
        173
        174
        175/**
        176 * Whether the code is running on the desktop Safari web browser.
        177 * Note: the legacy behavior here is only true for Safari not running
        178 * on iOS.
        179 * @type {boolean}
        180 */
        181goog.userAgent.product.SAFARI = goog.userAgent.product.PRODUCT_KNOWN_ ?
        182 goog.userAgent.product.ASSUME_SAFARI :
        183 goog.userAgent.product.isSafariDesktop_();
        \ No newline at end of file diff --git a/docs/source/lib/goog/useragent/product_isversion.js.src.html b/docs/source/lib/goog/useragent/product_isversion.js.src.html index 4f99683..6b9f54b 100644 --- a/docs/source/lib/goog/useragent/product_isversion.js.src.html +++ b/docs/source/lib/goog/useragent/product_isversion.js.src.html @@ -1 +1 @@ -product_isversion.js

        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
        26goog.provide('goog.userAgent.product.isVersion');
        27
        28
        29goog.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 */
        38goog.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 */
        104goog.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 */
        116goog.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 */
        126goog.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 */
        137goog.userAgent.product.isVersion = function(version) {
        138 return goog.string.compareVersions(
        139 goog.userAgent.product.VERSION, version) >= 0;
        140};
        \ No newline at end of file +product_isversion.js

        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
        26goog.provide('goog.userAgent.product.isVersion');
        27
        28
        29goog.require('goog.labs.userAgent.platform');
        30goog.require('goog.string');
        31goog.require('goog.userAgent');
        32goog.require('goog.userAgent.product');
        33
        34
        35/**
        36 * @return {string} The string that describes the version number of the user
        37 * agent product. This is a string rather than a number because it may
        38 * contain 'b', 'a', and so on.
        39 * @private
        40 */
        41goog.userAgent.product.determineVersion_ = function() {
        42 // All browsers have different ways to detect the version and they all have
        43 // different naming schemes.
        44
        45 if (goog.userAgent.product.FIREFOX) {
        46 // Firefox/2.0.0.1 or Firefox/3.5.3
        47 return goog.userAgent.product.getFirstRegExpGroup_(/Firefox\/([0-9.]+)/);
        48 }
        49
        50 if (goog.userAgent.product.IE || goog.userAgent.product.EDGE ||
        51 goog.userAgent.product.OPERA) {
        52 return goog.userAgent.VERSION;
        53 }
        54
        55 if (goog.userAgent.product.CHROME) {
        56 // Chrome/4.0.223.1
        57 return goog.userAgent.product.getFirstRegExpGroup_(/Chrome\/([0-9.]+)/);
        58 }
        59
        60 // This replicates legacy logic, which considered Safari and iOS to be
        61 // different products.
        62 if (goog.userAgent.product.SAFARI && !goog.labs.userAgent.platform.isIos()) {
        63 // Version/5.0.3
        64 //
        65 // NOTE: Before version 3, Safari did not report a product version number.
        66 // The product version number for these browsers will be the empty string.
        67 // They may be differentiated by WebKit version number in goog.userAgent.
        68 return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/);
        69 }
        70
        71 if (goog.userAgent.product.IPHONE || goog.userAgent.product.IPAD) {
        72 // Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1
        73 // (KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3
        74 // Version is the browser version, Mobile is the build number. We combine
        75 // the version string with the build number: 3.0.3A100a for the example.
        76 var arr = goog.userAgent.product.execRegExp_(
        77 /Version\/(\S+).*Mobile\/(\S+)/);
        78 if (arr) {
        79 return arr[1] + '.' + arr[2];
        80 }
        81 } else if (goog.userAgent.product.ANDROID) {
        82 // Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522+
        83 // (KHTML, like Gecko) Safari/419.3
        84 //
        85 // Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+
        86 // (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2
        87 //
        88 // Prefer Version number if present, else make do with the OS number
        89 var version = goog.userAgent.product.getFirstRegExpGroup_(
        90 /Android\s+([0-9.]+)/);
        91 if (version) {
        92 return version;
        93 }
        94
        95 return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/);
        96 }
        97
        98 return '';
        99};
        100
        101
        102/**
        103 * Return the first group of the given regex.
        104 * @param {!RegExp} re Regular expression with at least one group.
        105 * @return {string} Contents of the first group or an empty string if no match.
        106 * @private
        107 */
        108goog.userAgent.product.getFirstRegExpGroup_ = function(re) {
        109 var arr = goog.userAgent.product.execRegExp_(re);
        110 return arr ? arr[1] : '';
        111};
        112
        113
        114/**
        115 * Run regexp's exec() on the userAgent string.
        116 * @param {!RegExp} re Regular expression.
        117 * @return {Array<?>} A result array, or null for no match.
        118 * @private
        119 */
        120goog.userAgent.product.execRegExp_ = function(re) {
        121 return re.exec(goog.userAgent.getUserAgentString());
        122};
        123
        124
        125/**
        126 * The version of the user agent. This is a string because it might contain
        127 * 'b' (as in beta) as well as multiple dots.
        128 * @type {string}
        129 */
        130goog.userAgent.product.VERSION = goog.userAgent.product.determineVersion_();
        131
        132
        133/**
        134 * Whether the user agent product version is higher or the same as the given
        135 * version.
        136 *
        137 * @param {string|number} version The version to check.
        138 * @return {boolean} Whether the user agent product version is higher or the
        139 * same as the given version.
        140 */
        141goog.userAgent.product.isVersion = function(version) {
        142 return goog.string.compareVersions(
        143 goog.userAgent.product.VERSION, version) >= 0;
        144};
        \ No newline at end of file diff --git a/docs/source/lib/goog/useragent/useragent.js.src.html b/docs/source/lib/goog/useragent/useragent.js.src.html index 3f33e48..f121aa3 100644 --- a/docs/source/lib/goog/useragent/useragent.js.src.html +++ b/docs/source/lib/goog/useragent/useragent.js.src.html @@ -1 +1 @@ -useragent.js

        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
        23goog.provide('goog.userAgent');
        24
        25goog.require('goog.string');
        26
        27
        28/**
        29 * @define {boolean} Whether we know at compile-time that the browser is IE.
        30 */
        31goog.define('goog.userAgent.ASSUME_IE', false);
        32
        33
        34/**
        35 * @define {boolean} Whether we know at compile-time that the browser is GECKO.
        36 */
        37goog.define('goog.userAgent.ASSUME_GECKO', false);
        38
        39
        40/**
        41 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
        42 */
        43goog.define('goog.userAgent.ASSUME_WEBKIT', false);
        44
        45
        46/**
        47 * @define {boolean} Whether we know at compile-time that the browser is a
        48 * mobile device running WebKit e.g. iPhone or Android.
        49 */
        50goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
        51
        52
        53/**
        54 * @define {boolean} Whether we know at compile-time that the browser is OPERA.
        55 */
        56goog.define('goog.userAgent.ASSUME_OPERA', false);
        57
        58
        59/**
        60 * @define {boolean} Whether the
        61 * {@code goog.userAgent.isVersionOrHigher}
        62 * function will return true for any version.
        63 */
        64goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
        65
        66
        67/**
        68 * Whether we know the browser engine at compile-time.
        69 * @type {boolean}
        70 * @private
        71 */
        72goog.userAgent.BROWSER_KNOWN_ =
        73 goog.userAgent.ASSUME_IE ||
        74 goog.userAgent.ASSUME_GECKO ||
        75 goog.userAgent.ASSUME_MOBILE_WEBKIT ||
        76 goog.userAgent.ASSUME_WEBKIT ||
        77 goog.userAgent.ASSUME_OPERA;
        78
        79
        80/**
        81 * Returns the userAgent string for the current browser.
        82 * Some user agents (I'm thinking of you, Gears WorkerPool) do not expose a
        83 * navigator object off the global scope. In that case we return null.
        84 *
        85 * @return {?string} The userAgent string or null if there is none.
        86 */
        87goog.userAgent.getUserAgentString = function() {
        88 return goog.global['navigator'] ? goog.global['navigator'].userAgent : null;
        89};
        90
        91
        92/**
        93 * @return {Object} The native navigator object.
        94 */
        95goog.userAgent.getNavigator = function() {
        96 // Need a local navigator reference instead of using the global one,
        97 // to avoid the rare case where they reference different objects.
        98 // (in a WorkerPool, for example).
        99 return goog.global['navigator'];
        100};
        101
        102
        103/**
        104 * Initializer for goog.userAgent.
        105 *
        106 * This is a named function so that it can be stripped via the jscompiler
        107 * option for stripping types.
        108 * @private
        109 */
        110goog.userAgent.init_ = function() {
        111 /**
        112 * Whether the user agent string denotes Opera.
        113 * @type {boolean}
        114 * @private
        115 */
        116 goog.userAgent.detectedOpera_ = false;
        117
        118 /**
        119 * Whether the user agent string denotes Internet Explorer. This includes
        120 * other browsers using Trident as its rendering engine. For example AOL
        121 * and Netscape 8
        122 * @type {boolean}
        123 * @private
        124 */
        125 goog.userAgent.detectedIe_ = false;
        126
        127 /**
        128 * Whether the user agent string denotes WebKit. WebKit is the rendering
        129 * engine that Safari, Android and others use.
        130 * @type {boolean}
        131 * @private
        132 */
        133 goog.userAgent.detectedWebkit_ = false;
        134
        135 /**
        136 * Whether the user agent string denotes a mobile device.
        137 * @type {boolean}
        138 * @private
        139 */
        140 goog.userAgent.detectedMobile_ = false;
        141
        142 /**
        143 * Whether the user agent string denotes Gecko. Gecko is the rendering
        144 * engine used by Mozilla, Mozilla Firefox, Camino and many more.
        145 * @type {boolean}
        146 * @private
        147 */
        148 goog.userAgent.detectedGecko_ = false;
        149
        150 var ua;
        151 if (!goog.userAgent.BROWSER_KNOWN_ &&
        152 (ua = goog.userAgent.getUserAgentString())) {
        153 var navigator = goog.userAgent.getNavigator();
        154 goog.userAgent.detectedOpera_ = ua.indexOf('Opera') == 0;
        155 goog.userAgent.detectedIe_ = !goog.userAgent.detectedOpera_ &&
        156 ua.indexOf('MSIE') != -1;
        157 goog.userAgent.detectedWebkit_ = !goog.userAgent.detectedOpera_ &&
        158 ua.indexOf('WebKit') != -1;
        159 // WebKit also gives navigator.product string equal to 'Gecko'.
        160 goog.userAgent.detectedMobile_ = goog.userAgent.detectedWebkit_ &&
        161 ua.indexOf('Mobile') != -1;
        162 goog.userAgent.detectedGecko_ = !goog.userAgent.detectedOpera_ &&
        163 !goog.userAgent.detectedWebkit_ && navigator.product == 'Gecko';
        164 }
        165};
        166
        167
        168if (!goog.userAgent.BROWSER_KNOWN_) {
        169 goog.userAgent.init_();
        170}
        171
        172
        173/**
        174 * Whether the user agent is Opera.
        175 * @type {boolean}
        176 */
        177goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
        178 goog.userAgent.ASSUME_OPERA : goog.userAgent.detectedOpera_;
        179
        180
        181/**
        182 * Whether the user agent is Internet Explorer. This includes other browsers
        183 * using Trident as its rendering engine. For example AOL and Netscape 8
        184 * @type {boolean}
        185 */
        186goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
        187 goog.userAgent.ASSUME_IE : goog.userAgent.detectedIe_;
        188
        189
        190/**
        191 * Whether the user agent is Gecko. Gecko is the rendering engine used by
        192 * Mozilla, Mozilla Firefox, Camino and many more.
        193 * @type {boolean}
        194 */
        195goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
        196 goog.userAgent.ASSUME_GECKO :
        197 goog.userAgent.detectedGecko_;
        198
        199
        200/**
        201 * Whether the user agent is WebKit. WebKit is the rendering engine that
        202 * Safari, Android and others use.
        203 * @type {boolean}
        204 */
        205goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
        206 goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
        207 goog.userAgent.detectedWebkit_;
        208
        209
        210/**
        211 * Whether the user agent is running on a mobile device.
        212 * @type {boolean}
        213 */
        214goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||
        215 goog.userAgent.detectedMobile_;
        216
        217
        218/**
        219 * Used while transitioning code to use WEBKIT instead.
        220 * @type {boolean}
        221 * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
        222 * TODO(nicksantos): Delete this from goog.userAgent.
        223 */
        224goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
        225
        226
        227/**
        228 * @return {string} the platform (operating system) the user agent is running
        229 * on. Default to empty string because navigator.platform may not be defined
        230 * (on Rhino, for example).
        231 * @private
        232 */
        233goog.userAgent.determinePlatform_ = function() {
        234 var navigator = goog.userAgent.getNavigator();
        235 return navigator && navigator.platform || '';
        236};
        237
        238
        239/**
        240 * The platform (operating system) the user agent is running on. Default to
        241 * empty string because navigator.platform may not be defined (on Rhino, for
        242 * example).
        243 * @type {string}
        244 */
        245goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
        246
        247
        248/**
        249 * @define {boolean} Whether the user agent is running on a Macintosh operating
        250 * system.
        251 */
        252goog.define('goog.userAgent.ASSUME_MAC', false);
        253
        254
        255/**
        256 * @define {boolean} Whether the user agent is running on a Windows operating
        257 * system.
        258 */
        259goog.define('goog.userAgent.ASSUME_WINDOWS', false);
        260
        261
        262/**
        263 * @define {boolean} Whether the user agent is running on a Linux operating
        264 * system.
        265 */
        266goog.define('goog.userAgent.ASSUME_LINUX', false);
        267
        268
        269/**
        270 * @define {boolean} Whether the user agent is running on a X11 windowing
        271 * system.
        272 */
        273goog.define('goog.userAgent.ASSUME_X11', false);
        274
        275
        276/**
        277 * @define {boolean} Whether the user agent is running on Android.
        278 */
        279goog.define('goog.userAgent.ASSUME_ANDROID', false);
        280
        281
        282/**
        283 * @define {boolean} Whether the user agent is running on an iPhone.
        284 */
        285goog.define('goog.userAgent.ASSUME_IPHONE', false);
        286
        287
        288/**
        289 * @define {boolean} Whether the user agent is running on an iPad.
        290 */
        291goog.define('goog.userAgent.ASSUME_IPAD', false);
        292
        293
        294/**
        295 * @type {boolean}
        296 * @private
        297 */
        298goog.userAgent.PLATFORM_KNOWN_ =
        299 goog.userAgent.ASSUME_MAC ||
        300 goog.userAgent.ASSUME_WINDOWS ||
        301 goog.userAgent.ASSUME_LINUX ||
        302 goog.userAgent.ASSUME_X11 ||
        303 goog.userAgent.ASSUME_ANDROID ||
        304 goog.userAgent.ASSUME_IPHONE ||
        305 goog.userAgent.ASSUME_IPAD;
        306
        307
        308/**
        309 * Initialize the goog.userAgent constants that define which platform the user
        310 * agent is running on.
        311 * @private
        312 */
        313goog.userAgent.initPlatform_ = function() {
        314 /**
        315 * Whether the user agent is running on a Macintosh operating system.
        316 * @type {boolean}
        317 * @private
        318 */
        319 goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM,
        320 'Mac');
        321
        322 /**
        323 * Whether the user agent is running on a Windows operating system.
        324 * @type {boolean}
        325 * @private
        326 */
        327 goog.userAgent.detectedWindows_ = goog.string.contains(
        328 goog.userAgent.PLATFORM, 'Win');
        329
        330 /**
        331 * Whether the user agent is running on a Linux operating system.
        332 * @type {boolean}
        333 * @private
        334 */
        335 goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM,
        336 'Linux');
        337
        338 /**
        339 * Whether the user agent is running on a X11 windowing system.
        340 * @type {boolean}
        341 * @private
        342 */
        343 goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() &&
        344 goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '',
        345 'X11');
        346
        347 // Need user agent string for Android/IOS detection
        348 var ua = goog.userAgent.getUserAgentString();
        349
        350 /**
        351 * Whether the user agent is running on Android.
        352 * @type {boolean}
        353 * @private
        354 */
        355 goog.userAgent.detectedAndroid_ = !!ua && ua.indexOf('Android') >= 0;
        356
        357 /**
        358 * Whether the user agent is running on an iPhone.
        359 * @type {boolean}
        360 * @private
        361 */
        362 goog.userAgent.detectedIPhone_ = !!ua && ua.indexOf('iPhone') >= 0;
        363
        364 /**
        365 * Whether the user agent is running on an iPad.
        366 * @type {boolean}
        367 * @private
        368 */
        369 goog.userAgent.detectedIPad_ = !!ua && ua.indexOf('iPad') >= 0;
        370};
        371
        372
        373if (!goog.userAgent.PLATFORM_KNOWN_) {
        374 goog.userAgent.initPlatform_();
        375}
        376
        377
        378/**
        379 * Whether the user agent is running on a Macintosh operating system.
        380 * @type {boolean}
        381 */
        382goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
        383 goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_;
        384
        385
        386/**
        387 * Whether the user agent is running on a Windows operating system.
        388 * @type {boolean}
        389 */
        390goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
        391 goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_;
        392
        393
        394/**
        395 * Whether the user agent is running on a Linux operating system.
        396 * @type {boolean}
        397 */
        398goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
        399 goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_;
        400
        401
        402/**
        403 * Whether the user agent is running on a X11 windowing system.
        404 * @type {boolean}
        405 */
        406goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
        407 goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_;
        408
        409
        410/**
        411 * Whether the user agent is running on Android.
        412 * @type {boolean}
        413 */
        414goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
        415 goog.userAgent.ASSUME_ANDROID : goog.userAgent.detectedAndroid_;
        416
        417
        418/**
        419 * Whether the user agent is running on an iPhone.
        420 * @type {boolean}
        421 */
        422goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
        423 goog.userAgent.ASSUME_IPHONE : goog.userAgent.detectedIPhone_;
        424
        425
        426/**
        427 * Whether the user agent is running on an iPad.
        428 * @type {boolean}
        429 */
        430goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
        431 goog.userAgent.ASSUME_IPAD : goog.userAgent.detectedIPad_;
        432
        433
        434/**
        435 * @return {string} The string that describes the version number of the user
        436 * agent.
        437 * @private
        438 */
        439goog.userAgent.determineVersion_ = function() {
        440 // All browsers have different ways to detect the version and they all have
        441 // different naming schemes.
        442
        443 // version is a string rather than a number because it may contain 'b', 'a',
        444 // and so on.
        445 var version = '', re;
        446
        447 if (goog.userAgent.OPERA && goog.global['opera']) {
        448 var operaVersion = goog.global['opera'].version;
        449 version = typeof operaVersion == 'function' ? operaVersion() : operaVersion;
        450 } else {
        451 if (goog.userAgent.GECKO) {
        452 re = /rv\:([^\);]+)(\)|;)/;
        453 } else if (goog.userAgent.IE) {
        454 re = /MSIE\s+([^\);]+)(\)|;)/;
        455 } else if (goog.userAgent.WEBKIT) {
        456 // WebKit/125.4
        457 re = /WebKit\/(\S+)/;
        458 }
        459 if (re) {
        460 var arr = re.exec(goog.userAgent.getUserAgentString());
        461 version = arr ? arr[1] : '';
        462 }
        463 }
        464 if (goog.userAgent.IE) {
        465 // IE9 can be in document mode 9 but be reporting an inconsistent user agent
        466 // version. If it is identifying as a version lower than 9 we take the
        467 // documentMode as the version instead. IE8 has similar behavior.
        468 // It is recommended to set the X-UA-Compatible header to ensure that IE9
        469 // uses documentMode 9.
        470 var docMode = goog.userAgent.getDocumentMode_();
        471 if (docMode > parseFloat(version)) {
        472 return String(docMode);
        473 }
        474 }
        475 return version;
        476};
        477
        478
        479/**
        480 * @return {number|undefined} Returns the document mode (for testing).
        481 * @private
        482 */
        483goog.userAgent.getDocumentMode_ = function() {
        484 // NOTE(user): goog.userAgent may be used in context where there is no DOM.
        485 var doc = goog.global['document'];
        486 return doc ? doc['documentMode'] : undefined;
        487};
        488
        489
        490/**
        491 * The version of the user agent. This is a string because it might contain
        492 * 'b' (as in beta) as well as multiple dots.
        493 * @type {string}
        494 */
        495goog.userAgent.VERSION = goog.userAgent.determineVersion_();
        496
        497
        498/**
        499 * Compares two version numbers.
        500 *
        501 * @param {string} v1 Version of first item.
        502 * @param {string} v2 Version of second item.
        503 *
        504 * @return {number} 1 if first argument is higher
        505 * 0 if arguments are equal
        506 * -1 if second argument is higher.
        507 * @deprecated Use goog.string.compareVersions.
        508 */
        509goog.userAgent.compare = function(v1, v2) {
        510 return goog.string.compareVersions(v1, v2);
        511};
        512
        513
        514/**
        515 * Cache for {@link goog.userAgent.isVersionOrHigher}.
        516 * Calls to compareVersions are surprisingly expensive and, as a browser's
        517 * version number is unlikely to change during a session, we cache the results.
        518 * @const
        519 * @private
        520 */
        521goog.userAgent.isVersionOrHigherCache_ = {};
        522
        523
        524/**
        525 * Whether the user agent version is higher or the same as the given version.
        526 * NOTE: When checking the version numbers for Firefox and Safari, be sure to
        527 * use the engine's version, not the browser's version number. For example,
        528 * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
        529 * Opera and Internet Explorer versions match the product release number.<br>
        530 * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
        531 * Webkit</a>
        532 * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
        533 *
        534 * @param {string|number} version The version to check.
        535 * @return {boolean} Whether the user agent version is higher or the same as
        536 * the given version.
        537 */
        538goog.userAgent.isVersionOrHigher = function(version) {
        539 return goog.userAgent.ASSUME_ANY_VERSION ||
        540 goog.userAgent.isVersionOrHigherCache_[version] ||
        541 (goog.userAgent.isVersionOrHigherCache_[version] =
        542 goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);
        543};
        544
        545
        546/**
        547 * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
        548 * @param {string|number} version The version to check.
        549 * @return {boolean} Whether the user agent version is higher or the same as
        550 * the given version.
        551 * @deprecated Use goog.userAgent.isVersionOrHigher().
        552 */
        553goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
        554
        555
        556/**
        557 * Whether the IE effective document mode is higher or the same as the given
        558 * document mode version.
        559 * NOTE: Only for IE, return false for another browser.
        560 *
        561 * @param {number} documentMode The document mode version to check.
        562 * @return {boolean} Whether the IE effective document mode is higher or the
        563 * same as the given version.
        564 */
        565goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
        566 return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;
        567};
        568
        569
        570/**
        571 * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
        572 * @param {number} version The version to check.
        573 * @return {boolean} Whether the IE effective document mode is higher or the
        574 * same as the given version.
        575 */
        576goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
        577
        578
        579/**
        580 * For IE version < 7, documentMode is undefined, so attempt to use the
        581 * CSS1Compat property to see if we are in standards mode. If we are in
        582 * standards mode, treat the browser version as the document mode. Otherwise,
        583 * IE is emulating version 5.
        584 * @type {number|undefined}
        585 * @const
        586 */
        587goog.userAgent.DOCUMENT_MODE = (function() {
        588 var doc = goog.global['document'];
        589 if (!doc || !goog.userAgent.IE) {
        590 return undefined;
        591 }
        592 var mode = goog.userAgent.getDocumentMode_();
        593 return mode || (doc['compatMode'] == 'CSS1Compat' ?
        594 parseInt(goog.userAgent.VERSION, 10) : 5);
        595})();
        \ No newline at end of file +useragent.js

        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 * @author arv@google.com (Erik Arvidsson)
        21 * @see ../demos/useragent.html
        22 */
        23
        24goog.provide('goog.userAgent');
        25
        26goog.require('goog.labs.userAgent.browser');
        27goog.require('goog.labs.userAgent.engine');
        28goog.require('goog.labs.userAgent.platform');
        29goog.require('goog.labs.userAgent.util');
        30goog.require('goog.string');
        31
        32
        33/**
        34 * @define {boolean} Whether we know at compile-time that the browser is IE.
        35 */
        36goog.define('goog.userAgent.ASSUME_IE', false);
        37
        38
        39/**
        40 * @define {boolean} Whether we know at compile-time that the browser is EDGE.
        41 */
        42goog.define('goog.userAgent.ASSUME_EDGE', false);
        43
        44
        45/**
        46 * @define {boolean} Whether we know at compile-time that the browser is GECKO.
        47 */
        48goog.define('goog.userAgent.ASSUME_GECKO', false);
        49
        50
        51/**
        52 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
        53 */
        54goog.define('goog.userAgent.ASSUME_WEBKIT', false);
        55
        56
        57/**
        58 * @define {boolean} Whether we know at compile-time that the browser is a
        59 * mobile device running WebKit e.g. iPhone or Android.
        60 */
        61goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
        62
        63
        64/**
        65 * @define {boolean} Whether we know at compile-time that the browser is OPERA.
        66 */
        67goog.define('goog.userAgent.ASSUME_OPERA', false);
        68
        69
        70/**
        71 * @define {boolean} Whether the
        72 * {@code goog.userAgent.isVersionOrHigher}
        73 * function will return true for any version.
        74 */
        75goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
        76
        77
        78/**
        79 * Whether we know the browser engine at compile-time.
        80 * @type {boolean}
        81 * @private
        82 */
        83goog.userAgent.BROWSER_KNOWN_ =
        84 goog.userAgent.ASSUME_IE ||
        85 goog.userAgent.ASSUME_EDGE ||
        86 goog.userAgent.ASSUME_GECKO ||
        87 goog.userAgent.ASSUME_MOBILE_WEBKIT ||
        88 goog.userAgent.ASSUME_WEBKIT ||
        89 goog.userAgent.ASSUME_OPERA;
        90
        91
        92/**
        93 * Returns the userAgent string for the current browser.
        94 *
        95 * @return {string} The userAgent string.
        96 */
        97goog.userAgent.getUserAgentString = function() {
        98 return goog.labs.userAgent.util.getUserAgent();
        99};
        100
        101
        102/**
        103 * TODO(nnaze): Change type to "Navigator" and update compilation targets.
        104 * @return {Object} The native navigator object.
        105 */
        106goog.userAgent.getNavigator = function() {
        107 // Need a local navigator reference instead of using the global one,
        108 // to avoid the rare case where they reference different objects.
        109 // (in a WorkerPool, for example).
        110 return goog.global['navigator'] || null;
        111};
        112
        113
        114/**
        115 * Whether the user agent is Opera.
        116 * @type {boolean}
        117 */
        118goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
        119 goog.userAgent.ASSUME_OPERA :
        120 goog.labs.userAgent.browser.isOpera();
        121
        122
        123/**
        124 * Whether the user agent is Internet Explorer.
        125 * @type {boolean}
        126 */
        127goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
        128 goog.userAgent.ASSUME_IE :
        129 goog.labs.userAgent.browser.isIE();
        130
        131
        132/**
        133 * Whether the user agent is Microsoft Edge.
        134 * @type {boolean}
        135 */
        136goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ?
        137 goog.userAgent.ASSUME_EDGE :
        138 goog.labs.userAgent.engine.isEdge();
        139
        140
        141/**
        142 * Whether the user agent is MS Internet Explorer or MS Edge.
        143 * @type {boolean}
        144 */
        145goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE;
        146
        147
        148/**
        149 * Whether the user agent is Gecko. Gecko is the rendering engine used by
        150 * Mozilla, Firefox, and others.
        151 * @type {boolean}
        152 */
        153goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
        154 goog.userAgent.ASSUME_GECKO :
        155 goog.labs.userAgent.engine.isGecko();
        156
        157
        158/**
        159 * Whether the user agent is WebKit. WebKit is the rendering engine that
        160 * Safari, Android and others use.
        161 * @type {boolean}
        162 */
        163goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
        164 goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
        165 goog.labs.userAgent.engine.isWebKit();
        166
        167
        168/**
        169 * Whether the user agent is running on a mobile device.
        170 *
        171 * This is a separate function so that the logic can be tested.
        172 *
        173 * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().
        174 *
        175 * @return {boolean} Whether the user agent is running on a mobile device.
        176 * @private
        177 */
        178goog.userAgent.isMobile_ = function() {
        179 return goog.userAgent.WEBKIT &&
        180 goog.labs.userAgent.util.matchUserAgent('Mobile');
        181};
        182
        183
        184/**
        185 * Whether the user agent is running on a mobile device.
        186 *
        187 * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
        188 * is promoted as the gecko/webkit logic is likely inaccurate.
        189 *
        190 * @type {boolean}
        191 */
        192goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||
        193 goog.userAgent.isMobile_();
        194
        195
        196/**
        197 * Used while transitioning code to use WEBKIT instead.
        198 * @type {boolean}
        199 * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
        200 * TODO(nicksantos): Delete this from goog.userAgent.
        201 */
        202goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
        203
        204
        205/**
        206 * @return {string} the platform (operating system) the user agent is running
        207 * on. Default to empty string because navigator.platform may not be defined
        208 * (on Rhino, for example).
        209 * @private
        210 */
        211goog.userAgent.determinePlatform_ = function() {
        212 var navigator = goog.userAgent.getNavigator();
        213 return navigator && navigator.platform || '';
        214};
        215
        216
        217/**
        218 * The platform (operating system) the user agent is running on. Default to
        219 * empty string because navigator.platform may not be defined (on Rhino, for
        220 * example).
        221 * @type {string}
        222 */
        223goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
        224
        225
        226/**
        227 * @define {boolean} Whether the user agent is running on a Macintosh operating
        228 * system.
        229 */
        230goog.define('goog.userAgent.ASSUME_MAC', false);
        231
        232
        233/**
        234 * @define {boolean} Whether the user agent is running on a Windows operating
        235 * system.
        236 */
        237goog.define('goog.userAgent.ASSUME_WINDOWS', false);
        238
        239
        240/**
        241 * @define {boolean} Whether the user agent is running on a Linux operating
        242 * system.
        243 */
        244goog.define('goog.userAgent.ASSUME_LINUX', false);
        245
        246
        247/**
        248 * @define {boolean} Whether the user agent is running on a X11 windowing
        249 * system.
        250 */
        251goog.define('goog.userAgent.ASSUME_X11', false);
        252
        253
        254/**
        255 * @define {boolean} Whether the user agent is running on Android.
        256 */
        257goog.define('goog.userAgent.ASSUME_ANDROID', false);
        258
        259
        260/**
        261 * @define {boolean} Whether the user agent is running on an iPhone.
        262 */
        263goog.define('goog.userAgent.ASSUME_IPHONE', false);
        264
        265
        266/**
        267 * @define {boolean} Whether the user agent is running on an iPad.
        268 */
        269goog.define('goog.userAgent.ASSUME_IPAD', false);
        270
        271
        272/**
        273 * @type {boolean}
        274 * @private
        275 */
        276goog.userAgent.PLATFORM_KNOWN_ =
        277 goog.userAgent.ASSUME_MAC ||
        278 goog.userAgent.ASSUME_WINDOWS ||
        279 goog.userAgent.ASSUME_LINUX ||
        280 goog.userAgent.ASSUME_X11 ||
        281 goog.userAgent.ASSUME_ANDROID ||
        282 goog.userAgent.ASSUME_IPHONE ||
        283 goog.userAgent.ASSUME_IPAD;
        284
        285
        286/**
        287 * Whether the user agent is running on a Macintosh operating system.
        288 * @type {boolean}
        289 */
        290goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
        291 goog.userAgent.ASSUME_MAC : goog.labs.userAgent.platform.isMacintosh();
        292
        293
        294/**
        295 * Whether the user agent is running on a Windows operating system.
        296 * @type {boolean}
        297 */
        298goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
        299 goog.userAgent.ASSUME_WINDOWS :
        300 goog.labs.userAgent.platform.isWindows();
        301
        302
        303/**
        304 * Whether the user agent is Linux per the legacy behavior of
        305 * goog.userAgent.LINUX, which considered ChromeOS to also be
        306 * Linux.
        307 * @return {boolean}
        308 * @private
        309 */
        310goog.userAgent.isLegacyLinux_ = function() {
        311 return goog.labs.userAgent.platform.isLinux() ||
        312 goog.labs.userAgent.platform.isChromeOS();
        313};
        314
        315
        316/**
        317 * Whether the user agent is running on a Linux operating system.
        318 *
        319 * Note that goog.userAgent.LINUX considers ChromeOS to be Linux,
        320 * while goog.labs.userAgent.platform considers ChromeOS and
        321 * Linux to be different OSes.
        322 *
        323 * @type {boolean}
        324 */
        325goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
        326 goog.userAgent.ASSUME_LINUX :
        327 goog.userAgent.isLegacyLinux_();
        328
        329
        330/**
        331 * @return {boolean} Whether the user agent is an X11 windowing system.
        332 * @private
        333 */
        334goog.userAgent.isX11_ = function() {
        335 var navigator = goog.userAgent.getNavigator();
        336 return !!navigator &&
        337 goog.string.contains(navigator['appVersion'] || '', 'X11');
        338};
        339
        340
        341/**
        342 * Whether the user agent is running on a X11 windowing system.
        343 * @type {boolean}
        344 */
        345goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
        346 goog.userAgent.ASSUME_X11 :
        347 goog.userAgent.isX11_();
        348
        349
        350/**
        351 * Whether the user agent is running on Android.
        352 * @type {boolean}
        353 */
        354goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
        355 goog.userAgent.ASSUME_ANDROID :
        356 goog.labs.userAgent.platform.isAndroid();
        357
        358
        359/**
        360 * Whether the user agent is running on an iPhone.
        361 * @type {boolean}
        362 */
        363goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
        364 goog.userAgent.ASSUME_IPHONE :
        365 goog.labs.userAgent.platform.isIphone();
        366
        367
        368/**
        369 * Whether the user agent is running on an iPad.
        370 * @type {boolean}
        371 */
        372goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
        373 goog.userAgent.ASSUME_IPAD :
        374 goog.labs.userAgent.platform.isIpad();
        375
        376
        377/**
        378 * @return {string} The string that describes the version number of the user
        379 * agent.
        380 * Assumes user agent is opera.
        381 * @private
        382 */
        383goog.userAgent.operaVersion_ = function() {
        384 var version = goog.global.opera.version;
        385 try {
        386 return version();
        387 } catch (e) {
        388 return version;
        389 }
        390};
        391
        392
        393/**
        394 * @return {string} The string that describes the version number of the user
        395 * agent.
        396 * @private
        397 */
        398goog.userAgent.determineVersion_ = function() {
        399 // All browsers have different ways to detect the version and they all have
        400 // different naming schemes.
        401
        402 if (goog.userAgent.OPERA && goog.global['opera']) {
        403 return goog.userAgent.operaVersion_();
        404 }
        405
        406 // version is a string rather than a number because it may contain 'b', 'a',
        407 // and so on.
        408 var version = '';
        409 var arr = goog.userAgent.getVersionRegexResult_();
        410 if (arr) {
        411 version = arr ? arr[1] : '';
        412 }
        413
        414 if (goog.userAgent.IE) {
        415 // IE9 can be in document mode 9 but be reporting an inconsistent user agent
        416 // version. If it is identifying as a version lower than 9 we take the
        417 // documentMode as the version instead. IE8 has similar behavior.
        418 // It is recommended to set the X-UA-Compatible header to ensure that IE9
        419 // uses documentMode 9.
        420 var docMode = goog.userAgent.getDocumentMode_();
        421 if (docMode > parseFloat(version)) {
        422 return String(docMode);
        423 }
        424 }
        425
        426 return version;
        427};
        428
        429
        430/**
        431 * @return {Array|undefined} The version regex matches from parsing the user
        432 * agent string. These regex statements must be executed inline so they can
        433 * be compiled out by the closure compiler with the rest of the useragent
        434 * detection logic when ASSUME_* is specified.
        435 * @private
        436 */
        437goog.userAgent.getVersionRegexResult_ = function() {
        438 var userAgent = goog.userAgent.getUserAgentString();
        439 if (goog.userAgent.GECKO) {
        440 return /rv\:([^\);]+)(\)|;)/.exec(userAgent);
        441 }
        442 if (goog.userAgent.EDGE) {
        443 return /Edge\/([\d\.]+)/.exec(userAgent);
        444 }
        445 if (goog.userAgent.IE) {
        446 return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent);
        447 }
        448 if (goog.userAgent.WEBKIT) {
        449 // WebKit/125.4
        450 return /WebKit\/(\S+)/.exec(userAgent);
        451 }
        452};
        453
        454
        455/**
        456 * @return {number|undefined} Returns the document mode (for testing).
        457 * @private
        458 */
        459goog.userAgent.getDocumentMode_ = function() {
        460 // NOTE(user): goog.userAgent may be used in context where there is no DOM.
        461 var doc = goog.global['document'];
        462 return doc ? doc['documentMode'] : undefined;
        463};
        464
        465
        466/**
        467 * The version of the user agent. This is a string because it might contain
        468 * 'b' (as in beta) as well as multiple dots.
        469 * @type {string}
        470 */
        471goog.userAgent.VERSION = goog.userAgent.determineVersion_();
        472
        473
        474/**
        475 * Compares two version numbers.
        476 *
        477 * @param {string} v1 Version of first item.
        478 * @param {string} v2 Version of second item.
        479 *
        480 * @return {number} 1 if first argument is higher
        481 * 0 if arguments are equal
        482 * -1 if second argument is higher.
        483 * @deprecated Use goog.string.compareVersions.
        484 */
        485goog.userAgent.compare = function(v1, v2) {
        486 return goog.string.compareVersions(v1, v2);
        487};
        488
        489
        490/**
        491 * Cache for {@link goog.userAgent.isVersionOrHigher}.
        492 * Calls to compareVersions are surprisingly expensive and, as a browser's
        493 * version number is unlikely to change during a session, we cache the results.
        494 * @const
        495 * @private
        496 */
        497goog.userAgent.isVersionOrHigherCache_ = {};
        498
        499
        500/**
        501 * Whether the user agent version is higher or the same as the given version.
        502 * NOTE: When checking the version numbers for Firefox and Safari, be sure to
        503 * use the engine's version, not the browser's version number. For example,
        504 * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
        505 * Opera and Internet Explorer versions match the product release number.<br>
        506 * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
        507 * Webkit</a>
        508 * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
        509 *
        510 * @param {string|number} version The version to check.
        511 * @return {boolean} Whether the user agent version is higher or the same as
        512 * the given version.
        513 */
        514goog.userAgent.isVersionOrHigher = function(version) {
        515 return goog.userAgent.ASSUME_ANY_VERSION ||
        516 goog.userAgent.isVersionOrHigherCache_[version] ||
        517 (goog.userAgent.isVersionOrHigherCache_[version] =
        518 goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);
        519};
        520
        521
        522/**
        523 * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
        524 * @param {string|number} version The version to check.
        525 * @return {boolean} Whether the user agent version is higher or the same as
        526 * the given version.
        527 * @deprecated Use goog.userAgent.isVersionOrHigher().
        528 */
        529goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
        530
        531
        532/**
        533 * Whether the IE effective document mode is higher or the same as the given
        534 * document mode version.
        535 * NOTE: Only for IE, return false for another browser.
        536 *
        537 * @param {number} documentMode The document mode version to check.
        538 * @return {boolean} Whether the IE effective document mode is higher or the
        539 * same as the given version.
        540 */
        541goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
        542 return goog.userAgent.DOCUMENT_MODE >= documentMode;
        543};
        544
        545
        546/**
        547 * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
        548 * @param {number} version The version to check.
        549 * @return {boolean} Whether the IE effective document mode is higher or the
        550 * same as the given version.
        551 * @deprecated Use goog.userAgent.isDocumentModeOrHigher().
        552 */
        553goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
        554
        555
        556/**
        557 * For IE version < 7, documentMode is undefined, so attempt to use the
        558 * CSS1Compat property to see if we are in standards mode. If we are in
        559 * standards mode, treat the browser version as the document mode. Otherwise,
        560 * IE is emulating version 5.
        561 * @type {number|undefined}
        562 * @const
        563 */
        564goog.userAgent.DOCUMENT_MODE = (function() {
        565 var doc = goog.global['document'];
        566 var mode = goog.userAgent.getDocumentMode_();
        567 if (!doc || !goog.userAgent.IE) {
        568 return undefined;
        569 }
        570 return mode || (doc['compatMode'] == 'CSS1Compat' ?
        571 parseInt(goog.userAgent.VERSION, 10) : 5);
        572})();
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/abstractbuilder.js.src.html b/docs/source/lib/webdriver/abstractbuilder.js.src.html index 242c7a7..a0de85a 100644 --- a/docs/source/lib/webdriver/abstractbuilder.js.src.html +++ b/docs/source/lib/webdriver/abstractbuilder.js.src.html @@ -1 +1 @@ -abstractbuilder.js

        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
        15goog.provide('webdriver.AbstractBuilder');
        16
        17goog.require('webdriver.Capabilities');
        18goog.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 */
        33webdriver.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 */
        61webdriver.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 */
        70webdriver.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 */
        80webdriver.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 */
        90webdriver.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 */
        102webdriver.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 */
        112webdriver.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 */
        122webdriver.AbstractBuilder.prototype.build = goog.abstractMethod;
        \ No newline at end of file +abstractbuilder.js

        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
        15goog.provide('webdriver.AbstractBuilder');
        16
        17goog.require('webdriver.Capabilities');
        18goog.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 */
        33webdriver.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 */
        61webdriver.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 */
        70webdriver.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 */
        80webdriver.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 */
        90webdriver.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 */
        102webdriver.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 */
        112webdriver.AbstractBuilder.prototype.getCapabilities = function() {
        113 return this.capabilities_;
        114};
        115
        116
        117/**
        118 * Sets the logging preferences for the created session. Preferences may be
        119 * changed by repeated calls, or by calling {@link #withCapabilities}.
        120 * @param {!(webdriver.logging.Preferences|Object.<string, string>)} prefs The
        121 * desired logging preferences.
        122 * @return {!webdriver.AbstractBuilder} This Builder instance for chain calling.
        123 */
        124webdriver.AbstractBuilder.prototype.setLoggingPreferences = function(prefs) {
        125 this.capabilities_.set(webdriver.Capability.LOGGING_PREFS, prefs);
        126 return this;
        127};
        128
        129
        130/**
        131 * Builds a new {@link webdriver.WebDriver} instance using this builder's
        132 * current configuration.
        133 * @return {!webdriver.WebDriver} A new WebDriver client.
        134 */
        135webdriver.AbstractBuilder.prototype.build = goog.abstractMethod;
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/actionsequence.js.src.html b/docs/source/lib/webdriver/actionsequence.js.src.html index 711b4de..265273a 100644 --- a/docs/source/lib/webdriver/actionsequence.js.src.html +++ b/docs/source/lib/webdriver/actionsequence.js.src.html @@ -1 +1 @@ -actionsequence.js

        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
        16goog.provide('webdriver.ActionSequence');
        17
        18goog.require('goog.array');
        19goog.require('webdriver.Button');
        20goog.require('webdriver.Command');
        21goog.require('webdriver.CommandName');
        22goog.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 */
        43webdriver.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 */
        60webdriver.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 */
        73webdriver.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 */
        100webdriver.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 */
        144webdriver.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 */
        186webdriver.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 */
        213webdriver.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 */
        229webdriver.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 */
        250webdriver.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 */
        276webdriver.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 */
        291webdriver.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 */
        307webdriver.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 */
        324webdriver.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 */
        338webdriver.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 */
        353webdriver.ActionSequence.prototype.sendKeys = function(var_args) {
        354 var keys = goog.array.flatten(goog.array.slice(arguments, 0));
        355 return this.scheduleKeyboardAction_('sendKeys', keys);
        356};
        \ No newline at end of file +actionsequence.js

        lib/webdriver/actionsequence.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18goog.provide('webdriver.ActionSequence');
        19
        20goog.require('goog.array');
        21goog.require('webdriver.Button');
        22goog.require('webdriver.Command');
        23goog.require('webdriver.CommandName');
        24goog.require('webdriver.Key');
        25
        26
        27
        28/**
        29 * Class for defining sequences of complex user interactions. Each sequence
        30 * will not be executed until {@link #perform} is called.
        31 *
        32 * Example:
        33 *
        34 * new webdriver.ActionSequence(driver).
        35 * keyDown(webdriver.Key.SHIFT).
        36 * click(element1).
        37 * click(element2).
        38 * dragAndDrop(element3, element4).
        39 * keyUp(webdriver.Key.SHIFT).
        40 * perform();
        41 *
        42 * @param {!webdriver.WebDriver} driver The driver instance to use.
        43 * @constructor
        44 */
        45webdriver.ActionSequence = function(driver) {
        46
        47 /** @private {!webdriver.WebDriver} */
        48 this.driver_ = driver;
        49
        50 /** @private {!Array.<{description: string, command: !webdriver.Command}>} */
        51 this.actions_ = [];
        52};
        53
        54
        55/**
        56 * Schedules an action to be executed each time {@link #perform} is called on
        57 * this instance.
        58 * @param {string} description A description of the command.
        59 * @param {!webdriver.Command} command The command.
        60 * @private
        61 */
        62webdriver.ActionSequence.prototype.schedule_ = function(description, command) {
        63 this.actions_.push({
        64 description: description,
        65 command: command
        66 });
        67};
        68
        69
        70/**
        71 * Executes this action sequence.
        72 * @return {!webdriver.promise.Promise} A promise that will be resolved once
        73 * this sequence has completed.
        74 */
        75webdriver.ActionSequence.prototype.perform = function() {
        76 // Make a protected copy of the scheduled actions. This will protect against
        77 // users defining additional commands before this sequence is actually
        78 // executed.
        79 var actions = goog.array.clone(this.actions_);
        80 var driver = this.driver_;
        81 return driver.controlFlow().execute(function() {
        82 goog.array.forEach(actions, function(action) {
        83 driver.schedule(action.command, action.description);
        84 });
        85 }, 'ActionSequence.perform');
        86};
        87
        88
        89/**
        90 * Moves the mouse. The location to move to may be specified in terms of the
        91 * mouse's current location, an offset relative to the top-left corner of an
        92 * element, or an element (in which case the middle of the element is used).
        93 * @param {(!webdriver.WebElement|{x: number, y: number})} location The
        94 * location to drag to, as either another WebElement or an offset in pixels.
        95 * @param {{x: number, y: number}=} opt_offset If the target {@code location}
        96 * is defined as a {@link webdriver.WebElement}, this parameter defines an
        97 * offset within that element. The offset should be specified in pixels
        98 * relative to the top-left corner of the element's bounding box. If
        99 * omitted, the element's center will be used as the target offset.
        100 * @return {!webdriver.ActionSequence} A self reference.
        101 */
        102webdriver.ActionSequence.prototype.mouseMove = function(location, opt_offset) {
        103 var command = new webdriver.Command(webdriver.CommandName.MOVE_TO);
        104
        105 if (goog.isNumber(location.x)) {
        106 setOffset(/** @type {{x: number, y: number}} */(location));
        107 } else {
        108 command.setParameter('element', location.getRawId());
        109 if (opt_offset) {
        110 setOffset(opt_offset);
        111 }
        112 }
        113
        114 this.schedule_('mouseMove', command);
        115 return this;
        116
        117 /** @param {{x: number, y: number}} offset The offset to use. */
        118 function setOffset(offset) {
        119 command.setParameter('xoffset', offset.x || 0);
        120 command.setParameter('yoffset', offset.y || 0);
        121 }
        122};
        123
        124
        125/**
        126 * Schedules a mouse action.
        127 * @param {string} description A simple descriptive label for the scheduled
        128 * action.
        129 * @param {!webdriver.CommandName} commandName The name of the command.
        130 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either
        131 * the element to interact with or the button to click with.
        132 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor
        133 * button is specified.
        134 * @param {webdriver.Button=} opt_button The button to use. Defaults to
        135 * {@link webdriver.Button.LEFT}. Ignored if the previous argument is
        136 * provided as a button.
        137 * @return {!webdriver.ActionSequence} A self reference.
        138 * @private
        139 */
        140webdriver.ActionSequence.prototype.scheduleMouseAction_ = function(
        141 description, commandName, opt_elementOrButton, opt_button) {
        142 var button;
        143 if (goog.isNumber(opt_elementOrButton)) {
        144 button = opt_elementOrButton;
        145 } else {
        146 if (opt_elementOrButton) {
        147 this.mouseMove(
        148 /** @type {!webdriver.WebElement} */ (opt_elementOrButton));
        149 }
        150 button = goog.isDef(opt_button) ? opt_button : webdriver.Button.LEFT;
        151 }
        152
        153 var command = new webdriver.Command(commandName).
        154 setParameter('button', button);
        155 this.schedule_(description, command);
        156 return this;
        157};
        158
        159
        160/**
        161 * Presses a mouse button. The mouse button will not be released until
        162 * {@link #mouseUp} is called, regardless of whether that call is made in this
        163 * sequence or another. The behavior for out-of-order events (e.g. mouseDown,
        164 * click) is undefined.
        165 *
        166 * If an element is provided, the mouse will first be moved to the center
        167 * of that element. This is equivalent to:
        168 *
        169 * sequence.mouseMove(element).mouseDown()
        170 *
        171 * Warning: this method currently only supports the left mouse button. See
        172 * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
        173 *
        174 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either
        175 * the element to interact with or the button to click with.
        176 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor
        177 * button is specified.
        178 * @param {webdriver.Button=} opt_button The button to use. Defaults to
        179 * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the
        180 * first argument.
        181 * @return {!webdriver.ActionSequence} A self reference.
        182 */
        183webdriver.ActionSequence.prototype.mouseDown = function(opt_elementOrButton,
        184 opt_button) {
        185 return this.scheduleMouseAction_('mouseDown',
        186 webdriver.CommandName.MOUSE_DOWN, opt_elementOrButton, opt_button);
        187};
        188
        189
        190/**
        191 * Releases a mouse button. Behavior is undefined for calling this function
        192 * without a previous call to {@link #mouseDown}.
        193 *
        194 * If an element is provided, the mouse will first be moved to the center
        195 * of that element. This is equivalent to:
        196 *
        197 * sequence.mouseMove(element).mouseUp()
        198 *
        199 * Warning: this method currently only supports the left mouse button. See
        200 * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047).
        201 *
        202 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either
        203 * the element to interact with or the button to click with.
        204 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor
        205 * button is specified.
        206 * @param {webdriver.Button=} opt_button The button to use. Defaults to
        207 * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the
        208 * first argument.
        209 * @return {!webdriver.ActionSequence} A self reference.
        210 */
        211webdriver.ActionSequence.prototype.mouseUp = function(opt_elementOrButton,
        212 opt_button) {
        213 return this.scheduleMouseAction_('mouseUp',
        214 webdriver.CommandName.MOUSE_UP, opt_elementOrButton, opt_button);
        215};
        216
        217
        218/**
        219 * Convenience function for performing a "drag and drop" manuever. The target
        220 * element may be moved to the location of another element, or by an offset (in
        221 * pixels).
        222 * @param {!webdriver.WebElement} element The element to drag.
        223 * @param {(!webdriver.WebElement|{x: number, y: number})} location The
        224 * location to drag to, either as another WebElement or an offset in pixels.
        225 * @return {!webdriver.ActionSequence} A self reference.
        226 */
        227webdriver.ActionSequence.prototype.dragAndDrop = function(element, location) {
        228 return this.mouseDown(element).mouseMove(location).mouseUp();
        229};
        230
        231
        232/**
        233 * Clicks a mouse button.
        234 *
        235 * If an element is provided, the mouse will first be moved to the center
        236 * of that element. This is equivalent to:
        237 *
        238 * sequence.mouseMove(element).click()
        239 *
        240 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either
        241 * the element to interact with or the button to click with.
        242 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor
        243 * button is specified.
        244 * @param {webdriver.Button=} opt_button The button to use. Defaults to
        245 * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the
        246 * first argument.
        247 * @return {!webdriver.ActionSequence} A self reference.
        248 */
        249webdriver.ActionSequence.prototype.click = function(opt_elementOrButton,
        250 opt_button) {
        251 return this.scheduleMouseAction_('click',
        252 webdriver.CommandName.CLICK, opt_elementOrButton, opt_button);
        253};
        254
        255
        256/**
        257 * Double-clicks a mouse button.
        258 *
        259 * If an element is provided, the mouse will first be moved to the center of
        260 * that element. This is equivalent to:
        261 *
        262 * sequence.mouseMove(element).doubleClick()
        263 *
        264 * Warning: this method currently only supports the left mouse button. See
        265 * [issue 4047](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 */
        276webdriver.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 */
        291webdriver.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 */
        307webdriver.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 */
        324webdriver.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 */
        338webdriver.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 */
        353webdriver.ActionSequence.prototype.sendKeys = function(var_args) {
        354 var keys = goog.array.flatten(goog.array.slice(arguments, 0));
        355 return this.scheduleKeyboardAction_('sendKeys', keys);
        356};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/builder.js.src.html b/docs/source/lib/webdriver/builder.js.src.html index 8e10924..c51d70d 100644 --- a/docs/source/lib/webdriver/builder.js.src.html +++ b/docs/source/lib/webdriver/builder.js.src.html @@ -1 +1 @@ -builder.js

        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
        15goog.provide('webdriver.Builder');
        16
        17goog.require('goog.userAgent');
        18goog.require('webdriver.AbstractBuilder');
        19goog.require('webdriver.FirefoxDomExecutor');
        20goog.require('webdriver.WebDriver');
        21goog.require('webdriver.http.CorsClient');
        22goog.require('webdriver.http.Executor');
        23goog.require('webdriver.http.XhrClient');
        24goog.require('webdriver.process');
        25
        26
        27
        28/**
        29 * @constructor
        30 * @extends {webdriver.AbstractBuilder}
        31 */
        32webdriver.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};
        46goog.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 */
        60webdriver.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 */
        69webdriver.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 */
        79webdriver.Builder.prototype.getSession = function() {
        80 return this.sessionId_;
        81};
        82
        83
        84/**
        85 * @override
        86 */
        87webdriver.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};
        \ No newline at end of file +builder.js

        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
        15goog.provide('webdriver.Builder');
        16
        17goog.require('goog.userAgent');
        18goog.require('webdriver.AbstractBuilder');
        19goog.require('webdriver.FirefoxDomExecutor');
        20goog.require('webdriver.WebDriver');
        21goog.require('webdriver.http.CorsClient');
        22goog.require('webdriver.http.Executor');
        23goog.require('webdriver.http.XhrClient');
        24goog.require('webdriver.process');
        25
        26
        27
        28/**
        29 * @constructor
        30 * @extends {webdriver.AbstractBuilder}
        31 */
        32webdriver.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};
        46goog.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 */
        60webdriver.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 */
        69webdriver.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 */
        79webdriver.Builder.prototype.getSession = function() {
        80 return this.sessionId_;
        81};
        82
        83
        84/**
        85 * @override
        86 */
        87webdriver.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};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/button.js.src.html b/docs/source/lib/webdriver/button.js.src.html index 2a8728a..6d00b9a 100644 --- a/docs/source/lib/webdriver/button.js.src.html +++ b/docs/source/lib/webdriver/button.js.src.html @@ -1 +1 @@ -button.js

        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
        16goog.provide('webdriver.Button');
        17
        18
        19/**
        20 * Enumeration of the buttons used in the advanced interactions API.
        21 * @enum {number}
        22 */
        23webdriver.Button = {
        24 LEFT: 0,
        25 MIDDLE: 1,
        26 RIGHT: 2
        27};
        \ No newline at end of file +button.js

        lib/webdriver/button.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18goog.provide('webdriver.Button');
        19
        20
        21/**
        22 * Enumeration of the buttons used in the advanced interactions API.
        23 * @enum {number}
        24 */
        25webdriver.Button = {
        26 LEFT: 0,
        27 MIDDLE: 1,
        28 RIGHT: 2
        29};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/capabilities.js.src.html b/docs/source/lib/webdriver/capabilities.js.src.html index bd1a54b..4d1e7de 100644 --- a/docs/source/lib/webdriver/capabilities.js.src.html +++ b/docs/source/lib/webdriver/capabilities.js.src.html @@ -1 +1 @@ -capabilities.js

        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
        19goog.provide('webdriver.Browser');
        20goog.provide('webdriver.Capabilities');
        21goog.provide('webdriver.Capability');
        22
        23
        24
        25/**
        26 * Recognized browser names.
        27 * @enum {string}
        28 */
        29webdriver.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 */
        48webdriver.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 */
        138webdriver.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 */
        152webdriver.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 */
        162webdriver.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 */
        171webdriver.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 */
        181webdriver.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 */
        192webdriver.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 */
        202webdriver.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 */
        212webdriver.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 */
        222webdriver.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 */
        231webdriver.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 */
        240webdriver.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 */
        250webdriver.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. */
        258webdriver.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 */
        270webdriver.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 */
        288webdriver.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 */
        303webdriver.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 */
        316webdriver.Capabilities.prototype.has = function(key) {
        317 return !!this.get(key);
        318};
        \ No newline at end of file +capabilities.js

        lib/webdriver/capabilities.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines the webdriver.Capabilities class.
        20 */
        21
        22goog.provide('webdriver.Browser');
        23goog.provide('webdriver.Capabilities');
        24goog.provide('webdriver.Capability');
        25goog.provide('webdriver.ProxyConfig');
        26
        27goog.require('webdriver.Serializable');
        28goog.require('webdriver.logging');
        29
        30
        31
        32/**
        33 * Recognized browser names.
        34 * @enum {string}
        35 */
        36webdriver.Browser = {
        37 ANDROID: 'android',
        38 CHROME: 'chrome',
        39 FIREFOX: 'firefox',
        40 IE: 'internet explorer',
        41 INTERNET_EXPLORER: 'internet explorer',
        42 IPAD: 'iPad',
        43 IPHONE: 'iPhone',
        44 OPERA: 'opera',
        45 PHANTOM_JS: 'phantomjs',
        46 SAFARI: 'safari',
        47 HTMLUNIT: 'htmlunit'
        48};
        49
        50
        51
        52/**
        53 * Describes how a proxy should be configured for a WebDriver session.
        54 * Proxy configuration object, as defined by the WebDriver wire protocol.
        55 * @typedef {(
        56 * {proxyType: string}|
        57 * {proxyType: string,
        58 * proxyAutoconfigUrl: string}|
        59 * {proxyType: string,
        60 * ftpProxy: string,
        61 * httpProxy: string,
        62 * sslProxy: string,
        63 * noProxy: string})}
        64 */
        65webdriver.ProxyConfig;
        66
        67
        68
        69/**
        70 * Common webdriver capability keys.
        71 * @enum {string}
        72 */
        73webdriver.Capability = {
        74
        75 /**
        76 * Indicates whether a driver should accept all SSL certs by default. This
        77 * capability only applies when requesting a new session. To query whether
        78 * a driver can handle insecure SSL certs, see {@link #SECURE_SSL}.
        79 */
        80 ACCEPT_SSL_CERTS: 'acceptSslCerts',
        81
        82
        83 /**
        84 * The browser name. Common browser names are defined in the
        85 * {@link webdriver.Browser} enum.
        86 */
        87 BROWSER_NAME: 'browserName',
        88
        89 /**
        90 * Defines how elements should be scrolled into the viewport for interaction.
        91 * This capability will be set to zero (0) if elements are aligned with the
        92 * top of the viewport, or one (1) if aligned with the bottom. The default
        93 * behavior is to align with the top of the viewport.
        94 */
        95 ELEMENT_SCROLL_BEHAVIOR: 'elementScrollBehavior',
        96
        97 /**
        98 * Whether the driver is capable of handling modal alerts (e.g. alert,
        99 * confirm, prompt). To define how a driver <i>should</i> handle alerts,
        100 * use {@link #UNEXPECTED_ALERT_BEHAVIOR}.
        101 */
        102 HANDLES_ALERTS: 'handlesAlerts',
        103
        104 /**
        105 * Key for the logging driver logging preferences.
        106 */
        107 LOGGING_PREFS: 'loggingPrefs',
        108
        109 /**
        110 * Whether this session generates native events when simulating user input.
        111 */
        112 NATIVE_EVENTS: 'nativeEvents',
        113
        114 /**
        115 * Describes the platform the browser is running on. Will be one of
        116 * ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When <i>requesting</i> a
        117 * session, ANY may be used to indicate no platform preference (this is
        118 * semantically equivalent to omitting the platform capability).
        119 */
        120 PLATFORM: 'platform',
        121
        122 /**
        123 * Describes the proxy configuration to use for a new WebDriver session.
        124 */
        125 PROXY: 'proxy',
        126
        127 /** Whether the driver supports changing the brower's orientation. */
        128 ROTATABLE: 'rotatable',
        129
        130 /**
        131 * Whether a driver is only capable of handling secure SSL certs. To request
        132 * that a driver accept insecure SSL certs by default, use
        133 * {@link #ACCEPT_SSL_CERTS}.
        134 */
        135 SECURE_SSL: 'secureSsl',
        136
        137 /** Whether the driver supports manipulating the app cache. */
        138 SUPPORTS_APPLICATION_CACHE: 'applicationCacheEnabled',
        139
        140 /** Whether the driver supports locating elements with CSS selectors. */
        141 SUPPORTS_CSS_SELECTORS: 'cssSelectorsEnabled',
        142
        143 /** Whether the browser supports JavaScript. */
        144 SUPPORTS_JAVASCRIPT: 'javascriptEnabled',
        145
        146 /** Whether the driver supports controlling the browser's location info. */
        147 SUPPORTS_LOCATION_CONTEXT: 'locationContextEnabled',
        148
        149 /** Whether the driver supports taking screenshots. */
        150 TAKES_SCREENSHOT: 'takesScreenshot',
        151
        152 /**
        153 * Defines how the driver should handle unexpected alerts. The value should
        154 * be one of "accept", "dismiss", or "ignore.
        155 */
        156 UNEXPECTED_ALERT_BEHAVIOR: 'unexpectedAlertBehavior',
        157
        158 /** Defines the browser version. */
        159 VERSION: 'version'
        160};
        161
        162
        163
        164/**
        165 * @param {(webdriver.Capabilities|Object)=} opt_other Another set of
        166 * capabilities to merge into this instance.
        167 * @constructor
        168 * @extends {webdriver.Serializable.<!Object.<string, ?>>}
        169 */
        170webdriver.Capabilities = function(opt_other) {
        171 webdriver.Serializable.call(this);
        172
        173 /** @private {!Object.<string, ?>} */
        174 this.caps_ = {};
        175
        176 if (opt_other) {
        177 this.merge(opt_other);
        178 }
        179};
        180goog.inherits(webdriver.Capabilities, webdriver.Serializable);
        181
        182
        183/**
        184 * @return {!webdriver.Capabilities} A basic set of capabilities for Android.
        185 */
        186webdriver.Capabilities.android = function() {
        187 return new webdriver.Capabilities().
        188 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.ANDROID).
        189 set(webdriver.Capability.PLATFORM, 'ANDROID');
        190};
        191
        192
        193/**
        194 * @return {!webdriver.Capabilities} A basic set of capabilities for Chrome.
        195 */
        196webdriver.Capabilities.chrome = function() {
        197 return new webdriver.Capabilities().
        198 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.CHROME);
        199};
        200
        201
        202/**
        203 * @return {!webdriver.Capabilities} A basic set of capabilities for Firefox.
        204 */
        205webdriver.Capabilities.firefox = function() {
        206 return new webdriver.Capabilities().
        207 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.FIREFOX);
        208};
        209
        210
        211/**
        212 * @return {!webdriver.Capabilities} A basic set of capabilities for
        213 * Internet Explorer.
        214 */
        215webdriver.Capabilities.ie = function() {
        216 return new webdriver.Capabilities().
        217 set(webdriver.Capability.BROWSER_NAME,
        218 webdriver.Browser.INTERNET_EXPLORER).
        219 set(webdriver.Capability.PLATFORM, 'WINDOWS');
        220};
        221
        222
        223/**
        224 * @return {!webdriver.Capabilities} A basic set of capabilities for iPad.
        225 */
        226webdriver.Capabilities.ipad = function() {
        227 return new webdriver.Capabilities().
        228 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.IPAD).
        229 set(webdriver.Capability.PLATFORM, 'MAC');
        230};
        231
        232
        233/**
        234 * @return {!webdriver.Capabilities} A basic set of capabilities for iPhone.
        235 */
        236webdriver.Capabilities.iphone = function() {
        237 return new webdriver.Capabilities().
        238 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.IPHONE).
        239 set(webdriver.Capability.PLATFORM, 'MAC');
        240};
        241
        242
        243/**
        244 * @return {!webdriver.Capabilities} A basic set of capabilities for Opera.
        245 */
        246webdriver.Capabilities.opera = function() {
        247 return new webdriver.Capabilities().
        248 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.OPERA);
        249};
        250
        251/**
        252 * @return {!webdriver.Capabilities} A basic set of capabilities for
        253 * PhantomJS.
        254 */
        255webdriver.Capabilities.phantomjs = function() {
        256 return new webdriver.Capabilities().
        257 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.PHANTOM_JS);
        258};
        259
        260
        261/**
        262 * @return {!webdriver.Capabilities} A basic set of capabilities for Safari.
        263 */
        264webdriver.Capabilities.safari = function() {
        265 return new webdriver.Capabilities().
        266 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.SAFARI);
        267};
        268
        269
        270/**
        271 * @return {!webdriver.Capabilities} A basic set of capabilities for HTMLUnit.
        272 */
        273webdriver.Capabilities.htmlunit = function() {
        274 return new webdriver.Capabilities().
        275 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.HTMLUNIT);
        276};
        277
        278
        279/**
        280 * @return {!webdriver.Capabilities} A basic set of capabilities for HTMLUnit
        281 * with enabled Javascript.
        282 */
        283webdriver.Capabilities.htmlunitwithjs = function() {
        284 return new webdriver.Capabilities().
        285 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.HTMLUNIT).
        286 set(webdriver.Capability.SUPPORTS_JAVASCRIPT, true);
        287};
        288
        289
        290/**
        291 * @return {!Object.<string, ?>} The JSON representation of this instance. Note,
        292 * the returned object may contain nested promises that are promised values.
        293 * @override
        294 */
        295webdriver.Capabilities.prototype.serialize = function() {
        296 return this.caps_;
        297};
        298
        299
        300/**
        301 * Merges another set of capabilities into this instance. Any duplicates in
        302 * the provided set will override those already set on this instance.
        303 * @param {!(webdriver.Capabilities|Object)} other The capabilities to
        304 * merge into this instance.
        305 * @return {!webdriver.Capabilities} A self reference.
        306 */
        307webdriver.Capabilities.prototype.merge = function(other) {
        308 var caps = other instanceof webdriver.Capabilities ?
        309 other.caps_ : other;
        310 for (var key in caps) {
        311 if (caps.hasOwnProperty(key)) {
        312 this.set(key, caps[key]);
        313 }
        314 }
        315 return this;
        316};
        317
        318
        319/**
        320 * @param {string} key The capability to set.
        321 * @param {*} value The capability value. Capability values must be JSON
        322 * serializable. Pass {@code null} to unset the capability.
        323 * @return {!webdriver.Capabilities} A self reference.
        324 */
        325webdriver.Capabilities.prototype.set = function(key, value) {
        326 if (goog.isDefAndNotNull(value)) {
        327 this.caps_[key] = value;
        328 } else {
        329 delete this.caps_[key];
        330 }
        331 return this;
        332};
        333
        334
        335/**
        336 * @param {string} key The capability to return.
        337 * @return {*} The capability with the given key, or {@code null} if it has
        338 * not been set.
        339 */
        340webdriver.Capabilities.prototype.get = function(key) {
        341 var val = null;
        342 if (this.caps_.hasOwnProperty(key)) {
        343 val = this.caps_[key];
        344 }
        345 return goog.isDefAndNotNull(val) ? val : null;
        346};
        347
        348
        349/**
        350 * @param {string} key The capability to check.
        351 * @return {boolean} Whether the specified capability is set.
        352 */
        353webdriver.Capabilities.prototype.has = function(key) {
        354 return !!this.get(key);
        355};
        356
        357
        358/**
        359 * Sets the logging preferences. Preferences may be specified as a
        360 * {@link webdriver.logging.Preferences} instance, or a as a map of log-type to
        361 * log-level.
        362 * @param {!(webdriver.logging.Preferences|Object.<string, string>)} prefs The
        363 * logging preferences.
        364 * @return {!webdriver.Capabilities} A self reference.
        365 */
        366webdriver.Capabilities.prototype.setLoggingPrefs = function(prefs) {
        367 return this.set(webdriver.Capability.LOGGING_PREFS, prefs);
        368};
        369
        370
        371/**
        372 * Sets the proxy configuration for this instance.
        373 * @param {webdriver.ProxyConfig} proxy The desired proxy configuration.
        374 * @return {!webdriver.Capabilities} A self reference.
        375 */
        376webdriver.Capabilities.prototype.setProxy = function(proxy) {
        377 return this.set(webdriver.Capability.PROXY, proxy);
        378};
        379
        380
        381/**
        382 * Sets whether native events should be used.
        383 * @param {boolean} enabled Whether to enable native events.
        384 * @return {!webdriver.Capabilities} A self reference.
        385 */
        386webdriver.Capabilities.prototype.setEnableNativeEvents = function(enabled) {
        387 return this.set(webdriver.Capability.NATIVE_EVENTS, enabled);
        388};
        389
        390
        391/**
        392 * Sets how elements should be scrolled into view for interaction.
        393 * @param {number} behavior The desired scroll behavior: either 0 to align with
        394 * the top of the viewport or 1 to align with the bottom.
        395 * @return {!webdriver.Capabilities} A self reference.
        396 */
        397webdriver.Capabilities.prototype.setScrollBehavior = function(behavior) {
        398 return this.set(webdriver.Capability.ELEMENT_SCROLL_BEHAVIOR, behavior);
        399};
        400
        401
        402/**
        403 * Sets the default action to take with an unexpected alert before returning
        404 * an error.
        405 * @param {string} behavior The desired behavior; should be "accept", "dismiss",
        406 * or "ignore". Defaults to "dismiss".
        407 * @return {!webdriver.Capabilities} A self reference.
        408 */
        409webdriver.Capabilities.prototype.setAlertBehavior = function(behavior) {
        410 return this.set(webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR, behavior);
        411};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/command.js.src.html b/docs/source/lib/webdriver/command.js.src.html index 903630f..6eeaf6e 100644 --- a/docs/source/lib/webdriver/command.js.src.html +++ b/docs/source/lib/webdriver/command.js.src.html @@ -1 +1 @@ -command.js

        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
        19goog.provide('webdriver.Command');
        20goog.provide('webdriver.CommandExecutor');
        21goog.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 */
        30webdriver.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 */
        49webdriver.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 */
        60webdriver.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 */
        71webdriver.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 */
        82webdriver.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 */
        90webdriver.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.
        101webdriver.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: 'mouseDown',
        203 MOUSE_UP: 'mouseUp',
        204 MOVE_TO: 'mouseMove',
        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 */
        228webdriver.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 */
        240webdriver.CommandExecutor.prototype.execute = goog.abstractMethod;
        \ No newline at end of file +command.js

        lib/webdriver/command.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Contains several classes for handling commands.
        20 */
        21
        22goog.provide('webdriver.Command');
        23goog.provide('webdriver.CommandExecutor');
        24goog.provide('webdriver.CommandName');
        25
        26
        27
        28/**
        29 * Describes a command to be executed by the WebDriverJS framework.
        30 * @param {!webdriver.CommandName} name The name of this command.
        31 * @constructor
        32 */
        33webdriver.Command = function(name) {
        34
        35 /**
        36 * The name of this command.
        37 * @private {!webdriver.CommandName}
        38 */
        39 this.name_ = name;
        40
        41 /**
        42 * The parameters to this command.
        43 * @private {!Object.<*>}
        44 */
        45 this.parameters_ = {};
        46};
        47
        48
        49/**
        50 * @return {!webdriver.CommandName} This command's name.
        51 */
        52webdriver.Command.prototype.getName = function() {
        53 return this.name_;
        54};
        55
        56
        57/**
        58 * Sets a parameter to send with this command.
        59 * @param {string} name The parameter name.
        60 * @param {*} value The parameter value.
        61 * @return {!webdriver.Command} A self reference.
        62 */
        63webdriver.Command.prototype.setParameter = function(name, value) {
        64 this.parameters_[name] = value;
        65 return this;
        66};
        67
        68
        69/**
        70 * Sets the parameters for this command.
        71 * @param {!Object.<*>} parameters The command parameters.
        72 * @return {!webdriver.Command} A self reference.
        73 */
        74webdriver.Command.prototype.setParameters = function(parameters) {
        75 this.parameters_ = parameters;
        76 return this;
        77};
        78
        79
        80/**
        81 * Returns a named command parameter.
        82 * @param {string} key The parameter key to look up.
        83 * @return {*} The parameter value, or undefined if it has not been set.
        84 */
        85webdriver.Command.prototype.getParameter = function(key) {
        86 return this.parameters_[key];
        87};
        88
        89
        90/**
        91 * @return {!Object.<*>} The parameters to send with this command.
        92 */
        93webdriver.Command.prototype.getParameters = function() {
        94 return this.parameters_;
        95};
        96
        97
        98/**
        99 * Enumeration of predefined names command names that all command processors
        100 * will support.
        101 * @enum {string}
        102 */
        103// TODO: Delete obsolete command names.
        104webdriver.CommandName = {
        105 GET_SERVER_STATUS: 'getStatus',
        106
        107 NEW_SESSION: 'newSession',
        108 GET_SESSIONS: 'getSessions',
        109 DESCRIBE_SESSION: 'getSessionCapabilities',
        110
        111 CLOSE: 'close',
        112 QUIT: 'quit',
        113
        114 GET_CURRENT_URL: 'getCurrentUrl',
        115 GET: 'get',
        116 GO_BACK: 'goBack',
        117 GO_FORWARD: 'goForward',
        118 REFRESH: 'refresh',
        119
        120 ADD_COOKIE: 'addCookie',
        121 GET_COOKIE: 'getCookie',
        122 GET_ALL_COOKIES: 'getCookies',
        123 DELETE_COOKIE: 'deleteCookie',
        124 DELETE_ALL_COOKIES: 'deleteAllCookies',
        125
        126 GET_ACTIVE_ELEMENT: 'getActiveElement',
        127 FIND_ELEMENT: 'findElement',
        128 FIND_ELEMENTS: 'findElements',
        129 FIND_CHILD_ELEMENT: 'findChildElement',
        130 FIND_CHILD_ELEMENTS: 'findChildElements',
        131
        132 CLEAR_ELEMENT: 'clearElement',
        133 CLICK_ELEMENT: 'clickElement',
        134 SEND_KEYS_TO_ELEMENT: 'sendKeysToElement',
        135 SUBMIT_ELEMENT: 'submitElement',
        136
        137 GET_CURRENT_WINDOW_HANDLE: 'getCurrentWindowHandle',
        138 GET_WINDOW_HANDLES: 'getWindowHandles',
        139 GET_WINDOW_POSITION: 'getWindowPosition',
        140 SET_WINDOW_POSITION: 'setWindowPosition',
        141 GET_WINDOW_SIZE: 'getWindowSize',
        142 SET_WINDOW_SIZE: 'setWindowSize',
        143 MAXIMIZE_WINDOW: 'maximizeWindow',
        144
        145 SWITCH_TO_WINDOW: 'switchToWindow',
        146 SWITCH_TO_FRAME: 'switchToFrame',
        147 GET_PAGE_SOURCE: 'getPageSource',
        148 GET_TITLE: 'getTitle',
        149
        150 EXECUTE_SCRIPT: 'executeScript',
        151 EXECUTE_ASYNC_SCRIPT: 'executeAsyncScript',
        152
        153 GET_ELEMENT_TEXT: 'getElementText',
        154 GET_ELEMENT_TAG_NAME: 'getElementTagName',
        155 IS_ELEMENT_SELECTED: 'isElementSelected',
        156 IS_ELEMENT_ENABLED: 'isElementEnabled',
        157 IS_ELEMENT_DISPLAYED: 'isElementDisplayed',
        158 GET_ELEMENT_LOCATION: 'getElementLocation',
        159 GET_ELEMENT_LOCATION_IN_VIEW: 'getElementLocationOnceScrolledIntoView',
        160 GET_ELEMENT_SIZE: 'getElementSize',
        161 GET_ELEMENT_ATTRIBUTE: 'getElementAttribute',
        162 GET_ELEMENT_VALUE_OF_CSS_PROPERTY: 'getElementValueOfCssProperty',
        163 ELEMENT_EQUALS: 'elementEquals',
        164
        165 SCREENSHOT: 'screenshot',
        166 IMPLICITLY_WAIT: 'implicitlyWait',
        167 SET_SCRIPT_TIMEOUT: 'setScriptTimeout',
        168 SET_TIMEOUT: 'setTimeout',
        169
        170 ACCEPT_ALERT: 'acceptAlert',
        171 DISMISS_ALERT: 'dismissAlert',
        172 GET_ALERT_TEXT: 'getAlertText',
        173 SET_ALERT_TEXT: 'setAlertValue',
        174
        175 EXECUTE_SQL: 'executeSQL',
        176 GET_LOCATION: 'getLocation',
        177 SET_LOCATION: 'setLocation',
        178 GET_APP_CACHE: 'getAppCache',
        179 GET_APP_CACHE_STATUS: 'getStatus',
        180 CLEAR_APP_CACHE: 'clearAppCache',
        181 IS_BROWSER_ONLINE: 'isBrowserOnline',
        182 SET_BROWSER_ONLINE: 'setBrowserOnline',
        183
        184 GET_LOCAL_STORAGE_ITEM: 'getLocalStorageItem',
        185 GET_LOCAL_STORAGE_KEYS: 'getLocalStorageKeys',
        186 SET_LOCAL_STORAGE_ITEM: 'setLocalStorageItem',
        187 REMOVE_LOCAL_STORAGE_ITEM: 'removeLocalStorageItem',
        188 CLEAR_LOCAL_STORAGE: 'clearLocalStorage',
        189 GET_LOCAL_STORAGE_SIZE: 'getLocalStorageSize',
        190
        191 GET_SESSION_STORAGE_ITEM: 'getSessionStorageItem',
        192 GET_SESSION_STORAGE_KEYS: 'getSessionStorageKey',
        193 SET_SESSION_STORAGE_ITEM: 'setSessionStorageItem',
        194 REMOVE_SESSION_STORAGE_ITEM: 'removeSessionStorageItem',
        195 CLEAR_SESSION_STORAGE: 'clearSessionStorage',
        196 GET_SESSION_STORAGE_SIZE: 'getSessionStorageSize',
        197
        198 SET_SCREEN_ORIENTATION: 'setScreenOrientation',
        199 GET_SCREEN_ORIENTATION: 'getScreenOrientation',
        200
        201 // These belong to the Advanced user interactions - an element is
        202 // optional for these commands.
        203 CLICK: 'mouseClick',
        204 DOUBLE_CLICK: 'mouseDoubleClick',
        205 MOUSE_DOWN: 'mouseButtonDown',
        206 MOUSE_UP: 'mouseButtonUp',
        207 MOVE_TO: 'mouseMoveTo',
        208 SEND_KEYS_TO_ACTIVE_ELEMENT: 'sendKeysToActiveElement',
        209
        210 // These belong to the Advanced Touch API
        211 TOUCH_SINGLE_TAP: 'touchSingleTap',
        212 TOUCH_DOWN: 'touchDown',
        213 TOUCH_UP: 'touchUp',
        214 TOUCH_MOVE: 'touchMove',
        215 TOUCH_SCROLL: 'touchScroll',
        216 TOUCH_DOUBLE_TAP: 'touchDoubleTap',
        217 TOUCH_LONG_PRESS: 'touchLongPress',
        218 TOUCH_FLICK: 'touchFlick',
        219
        220 GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes',
        221 GET_LOG: 'getLog',
        222 GET_SESSION_LOGS: 'getSessionLogs',
        223
        224 // Non-standard commands used by the standalone Selenium server.
        225 UPLOAD_FILE: 'uploadFile'
        226};
        227
        228
        229
        230/**
        231 * Handles the execution of WebDriver {@link webdriver.Command commands}.
        232 * @interface
        233 */
        234webdriver.CommandExecutor = function() {};
        235
        236
        237/**
        238 * Executes the given {@code command}. If there is an error executing the
        239 * command, the provided callback will be invoked with the offending error.
        240 * Otherwise, the callback will be invoked with a null Error and non-null
        241 * {@link bot.response.ResponseObject} object.
        242 * @param {!webdriver.Command} command The command to execute.
        243 * @param {function(Error, !bot.response.ResponseObject=)} callback the function
        244 * to invoke when the command response is ready.
        245 */
        246webdriver.CommandExecutor.prototype.execute = goog.abstractMethod;
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/events.js.src.html b/docs/source/lib/webdriver/events.js.src.html index c60d9c5..8d50f5d 100644 --- a/docs/source/lib/webdriver/events.js.src.html +++ b/docs/source/lib/webdriver/events.js.src.html @@ -1 +1 @@ -events.js

        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
        19goog.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 */
        29webdriver.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 */
        44webdriver.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 */
        71webdriver.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 */
        90webdriver.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 */
        116webdriver.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 */
        130webdriver.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 */
        142webdriver.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 */
        152webdriver.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 */
        173webdriver.EventEmitter.prototype.removeAllListeners = function(opt_type) {
        174 goog.isDef(opt_type) ? delete this.events_[opt_type] : this.events_ = {};
        175 return this;
        176};
        \ No newline at end of file +events.js

        lib/webdriver/events.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview A light weight event system modeled after Node's EventEmitter.
        20 */
        21
        22goog.provide('webdriver.EventEmitter');
        23
        24
        25
        26/**
        27 * Object that can emit events for others to listen for. This is used instead
        28 * of Closure's event system because it is much more light weight. The API is
        29 * based on Node's EventEmitters.
        30 * @constructor
        31 */
        32webdriver.EventEmitter = function() {
        33 /**
        34 * Map of events to registered listeners.
        35 * @private {!Object.<!Array.<{fn: !Function, oneshot: boolean,
        36 * scope: (Object|undefined)}>>}
        37 */
        38 this.events_ = {};
        39};
        40
        41
        42/**
        43 * Fires an event and calls all listeners.
        44 * @param {string} type The type of event to emit.
        45 * @param {...*} var_args Any arguments to pass to each listener.
        46 */
        47webdriver.EventEmitter.prototype.emit = function(type, var_args) {
        48 var args = Array.prototype.slice.call(arguments, 1);
        49 var listeners = this.events_[type];
        50 if (!listeners) {
        51 return;
        52 }
        53 for (var i = 0; i < listeners.length;) {
        54 var listener = listeners[i];
        55 listener.fn.apply(listener.scope, args);
        56 if (listeners[i] === listener) {
        57 if (listeners[i].oneshot) {
        58 listeners.splice(i, 1);
        59 } else {
        60 i += 1;
        61 }
        62 }
        63 }
        64};
        65
        66
        67/**
        68 * Returns a mutable list of listeners for a specific type of event.
        69 * @param {string} type The type of event to retrieve the listeners for.
        70 * @return {!Array.<{fn: !Function, oneshot: boolean,
        71 * scope: (Object|undefined)}>} The registered listeners for
        72 * the given event type.
        73 */
        74webdriver.EventEmitter.prototype.listeners = function(type) {
        75 var listeners = this.events_[type];
        76 if (!listeners) {
        77 listeners = this.events_[type] = [];
        78 }
        79 return listeners;
        80};
        81
        82
        83/**
        84 * Registers a listener.
        85 * @param {string} type The type of event to listen for.
        86 * @param {!Function} listenerFn The function to invoke when the event is fired.
        87 * @param {Object=} opt_scope The object in whose scope to invoke the listener.
        88 * @param {boolean=} opt_oneshot Whether the listener should be removed after
        89 * the first event is fired.
        90 * @return {!webdriver.EventEmitter} A self reference.
        91 * @private
        92 */
        93webdriver.EventEmitter.prototype.addListener_ = function(type, listenerFn,
        94 opt_scope, opt_oneshot) {
        95 var listeners = this.listeners(type);
        96 var n = listeners.length;
        97 for (var i = 0; i < n; ++i) {
        98 if (listeners[i].fn == listenerFn) {
        99 return this;
        100 }
        101 }
        102
        103 listeners.push({
        104 fn: listenerFn,
        105 scope: opt_scope,
        106 oneshot: !!opt_oneshot
        107 });
        108 return this;
        109};
        110
        111
        112/**
        113 * Registers a listener.
        114 * @param {string} type The type of event to listen for.
        115 * @param {!Function} listenerFn The function to invoke when the event is fired.
        116 * @param {Object=} opt_scope The object in whose scope to invoke the listener.
        117 * @return {!webdriver.EventEmitter} A self reference.
        118 */
        119webdriver.EventEmitter.prototype.addListener = function(type, listenerFn,
        120 opt_scope) {
        121 return this.addListener_(type, listenerFn, opt_scope);
        122};
        123
        124
        125/**
        126 * Registers a one-time listener which will be called only the first time an
        127 * event is emitted, after which it will be removed.
        128 * @param {string} type The type of event to listen for.
        129 * @param {!Function} listenerFn The function to invoke when the event is fired.
        130 * @param {Object=} opt_scope The object in whose scope to invoke the listener.
        131 * @return {!webdriver.EventEmitter} A self reference.
        132 */
        133webdriver.EventEmitter.prototype.once = function(type, listenerFn, opt_scope) {
        134 return this.addListener_(type, listenerFn, opt_scope, true);
        135};
        136
        137
        138/**
        139 * An alias for {@code #addListener()}.
        140 * @param {string} type The type of event to listen for.
        141 * @param {!Function} listenerFn The function to invoke when the event is fired.
        142 * @param {Object=} opt_scope The object in whose scope to invoke the listener.
        143 * @return {!webdriver.EventEmitter} A self reference.
        144 */
        145webdriver.EventEmitter.prototype.on =
        146 webdriver.EventEmitter.prototype.addListener;
        147
        148
        149/**
        150 * Removes a previously registered event listener.
        151 * @param {string} type The type of event to unregister.
        152 * @param {!Function} listenerFn The handler function to remove.
        153 * @return {!webdriver.EventEmitter} A self reference.
        154 */
        155webdriver.EventEmitter.prototype.removeListener = function(type, listenerFn) {
        156 var listeners = this.events_[type];
        157 if (listeners) {
        158 var n = listeners.length;
        159 for (var i = 0; i < n; ++i) {
        160 if (listeners[i].fn == listenerFn) {
        161 listeners.splice(i, 1);
        162 return this;
        163 }
        164 }
        165 }
        166 return this;
        167};
        168
        169
        170/**
        171 * Removes all listeners for a specific type of event. If no event is
        172 * specified, all listeners across all types will be removed.
        173 * @param {string=} opt_type The type of event to remove listeners from.
        174 * @return {!webdriver.EventEmitter} A self reference.
        175 */
        176webdriver.EventEmitter.prototype.removeAllListeners = function(opt_type) {
        177 goog.isDef(opt_type) ? delete this.events_[opt_type] : this.events_ = {};
        178 return this;
        179};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/firefoxdomexecutor.js.src.html b/docs/source/lib/webdriver/firefoxdomexecutor.js.src.html index a463d7e..3fb0708 100644 --- a/docs/source/lib/webdriver/firefoxdomexecutor.js.src.html +++ b/docs/source/lib/webdriver/firefoxdomexecutor.js.src.html @@ -1 +1 @@ -firefoxdomexecutor.js

        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
        15goog.provide('webdriver.FirefoxDomExecutor');
        16
        17goog.require('bot.response');
        18goog.require('goog.json');
        19goog.require('goog.userAgent.product');
        20goog.require('webdriver.Command');
        21goog.require('webdriver.CommandName');
        22
        23
        24
        25/**
        26 * @constructor
        27 * @implements {webdriver.CommandExecutor}
        28 */
        29webdriver.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 */
        51webdriver.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 */
        65webdriver.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 */
        76webdriver.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 */
        86webdriver.FirefoxDomExecutor.prototype.pendingCommand_ = null;
        87
        88
        89/** @override */
        90webdriver.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
        117 var json = goog.json.serialize({
        118 'name': command.getName(),
        119 'sessionId': {
        120 'value': parameters['sessionId']
        121 },
        122 'parameters': parameters
        123 });
        124 this.docElement_.setAttribute(
        125 webdriver.FirefoxDomExecutor.Attribute_.COMMAND, json);
        126
        127 var event = this.doc_.createEvent('Event');
        128 event.initEvent(webdriver.FirefoxDomExecutor.EventType_.COMMAND,
        129 /*canBubble=*/true, /*cancelable=*/true);
        130
        131 this.docElement_.dispatchEvent(event);
        132};
        133
        134
        135/** @private */
        136webdriver.FirefoxDomExecutor.prototype.onResponse_ = function() {
        137 if (!this.pendingCommand_) {
        138 return; // Not expecting a response.
        139 }
        140
        141 var command = this.pendingCommand_;
        142 this.pendingCommand_ = null;
        143
        144 var json = this.docElement_.getAttribute(
        145 webdriver.FirefoxDomExecutor.Attribute_.RESPONSE);
        146 if (!json) {
        147 command.callback(Error('Empty command response!'));
        148 return;
        149 }
        150
        151 this.docElement_.removeAttribute(
        152 webdriver.FirefoxDomExecutor.Attribute_.COMMAND);
        153 this.docElement_.removeAttribute(
        154 webdriver.FirefoxDomExecutor.Attribute_.RESPONSE);
        155
        156 try {
        157 var response = bot.response.checkResponse(
        158 /** @type {!bot.response.ResponseObject} */ (goog.json.parse(json)));
        159 } catch (ex) {
        160 command.callback(ex);
        161 return;
        162 }
        163
        164 // Prior to Selenium 2.35.0, two commands are required to fully create a
        165 // session: one to allocate the session, and another to fetch the
        166 // capabilities.
        167 if (command.name == webdriver.CommandName.NEW_SESSION &&
        168 goog.isString(response['value'])) {
        169 var cmd = new webdriver.Command(webdriver.CommandName.DESCRIBE_SESSION).
        170 setParameter('sessionId', response['value']);
        171 this.execute(cmd, command.callback);
        172 } else {
        173 command.callback(null, response);
        174 }
        175};
        \ No newline at end of file +firefoxdomexecutor.js

        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
        15goog.provide('webdriver.FirefoxDomExecutor');
        16
        17goog.require('bot.response');
        18goog.require('goog.json');
        19goog.require('goog.userAgent.product');
        20goog.require('webdriver.Command');
        21goog.require('webdriver.CommandName');
        22
        23
        24
        25/**
        26 * @constructor
        27 * @implements {webdriver.CommandExecutor}
        28 */
        29webdriver.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 */
        51webdriver.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 */
        65webdriver.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 */
        76webdriver.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 */
        86webdriver.FirefoxDomExecutor.prototype.pendingCommand_ = null;
        87
        88
        89/** @override */
        90webdriver.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 */
        133webdriver.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};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/http/corsclient.js.src.html b/docs/source/lib/webdriver/http/corsclient.js.src.html index f33da44..cb2a928 100644 --- a/docs/source/lib/webdriver/http/corsclient.js.src.html +++ b/docs/source/lib/webdriver/http/corsclient.js.src.html @@ -1 +1 @@ -corsclient.js

        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
        15goog.provide('webdriver.http.CorsClient');
        16
        17goog.require('goog.json');
        18goog.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 */
        64webdriver.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 */
        80webdriver.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 */
        88webdriver.http.CorsClient.isAvailable = function() {
        89 return typeof XDomainRequest !== 'undefined' ||
        90 (typeof XMLHttpRequest !== 'undefined' &&
        91 goog.isBoolean(new XMLHttpRequest().withCredentials));
        92};
        93
        94
        95/** @override */
        96webdriver.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};
        \ No newline at end of file +corsclient.js

        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
        15goog.provide('webdriver.http.CorsClient');
        16
        17goog.require('goog.json');
        18goog.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 */
        64webdriver.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 */
        80webdriver.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 */
        88webdriver.http.CorsClient.isAvailable = function() {
        89 return typeof XDomainRequest !== 'undefined' ||
        90 (typeof XMLHttpRequest !== 'undefined' &&
        91 goog.isBoolean(new XMLHttpRequest().withCredentials));
        92};
        93
        94
        95/** @override */
        96webdriver.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};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/http/http.js.src.html b/docs/source/lib/webdriver/http/http.js.src.html index 291e8c3..2ac026c 100644 --- a/docs/source/lib/webdriver/http/http.js.src.html +++ b/docs/source/lib/webdriver/http/http.js.src.html @@ -1 +1 @@ -http.js

        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
        20goog.provide('webdriver.http.Client');
        21goog.provide('webdriver.http.Executor');
        22goog.provide('webdriver.http.Request');
        23goog.provide('webdriver.http.Response');
        24
        25goog.require('bot.ErrorCode');
        26goog.require('goog.array');
        27goog.require('goog.json');
        28goog.require('webdriver.CommandName');
        29goog.require('webdriver.promise.Deferred');
        30
        31
        32
        33/**
        34 * Interface used for sending individual HTTP requests to the server.
        35 * @interface
        36 */
        37webdriver.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 */
        52webdriver.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 */
        65webdriver.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 */
        76webdriver.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 */
        112webdriver.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 */
        145webdriver.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 */
        176webdriver.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 */
        323webdriver.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 */
        343webdriver.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 */
        372webdriver.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 */
        391webdriver.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 */
        422webdriver.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 */
        449webdriver.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};
        \ No newline at end of file +http.js

        lib/webdriver/http/http.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines a {@code webdriver.CommandExecutor} that communicates
        20 * with a server over HTTP.
        21 */
        22
        23goog.provide('webdriver.http.Client');
        24goog.provide('webdriver.http.Executor');
        25goog.provide('webdriver.http.Request');
        26goog.provide('webdriver.http.Response');
        27
        28goog.require('bot.ErrorCode');
        29goog.require('goog.array');
        30goog.require('webdriver.CommandExecutor');
        31goog.require('webdriver.CommandName');
        32goog.require('webdriver.logging');
        33goog.require('webdriver.promise');
        34
        35
        36
        37/**
        38 * Interface used for sending individual HTTP requests to the server.
        39 * @interface
        40 */
        41webdriver.http.Client = function() {
        42};
        43
        44
        45/**
        46 * Sends a request to the server. If an error occurs while sending the request,
        47 * such as a failure to connect to the server, the provided callback will be
        48 * invoked with a non-null {@link Error} describing the error. Otherwise, when
        49 * the server's response has been received, the callback will be invoked with a
        50 * null Error and non-null {@link webdriver.http.Response} object.
        51 *
        52 * @param {!webdriver.http.Request} request The request to send.
        53 * @param {function(Error, !webdriver.http.Response=)} callback the function to
        54 * invoke when the server's response is ready.
        55 */
        56webdriver.http.Client.prototype.send = function(request, callback) {
        57};
        58
        59
        60
        61/**
        62 * A command executor that communicates with a server using the WebDriver
        63 * command protocol.
        64 * @param {!webdriver.http.Client} client The client to use when sending
        65 * requests to the server.
        66 * @constructor
        67 * @implements {webdriver.CommandExecutor}
        68 */
        69webdriver.http.Executor = function(client) {
        70
        71 /**
        72 * Client used to communicate with the server.
        73 * @private {!webdriver.http.Client}
        74 */
        75 this.client_ = client;
        76
        77 /**
        78 * @private {!Object<{method:string, path:string}>}
        79 */
        80 this.customCommands_ = {};
        81
        82 /**
        83 * @private {!webdriver.logging.Logger}
        84 */
        85 this.log_ = webdriver.logging.getLogger('webdriver.http.Executor');
        86};
        87
        88
        89/**
        90 * Defines a new command for use with this executor. When a command is sent,
        91 * the {@code path} will be preprocessed using the command's parameters; any
        92 * path segments prefixed with ":" will be replaced by the parameter of the
        93 * same name. For example, given "/person/:name" and the parameters
        94 * "{name: 'Bob'}", the final command path will be "/person/Bob".
        95 *
        96 * @param {string} name The command name.
        97 * @param {string} method The HTTP method to use when sending this command.
        98 * @param {string} path The path to send the command to, relative to
        99 * the WebDriver server's command root and of the form
        100 * "/path/:variable/segment".
        101 */
        102webdriver.http.Executor.prototype.defineCommand = function(
        103 name, method, path) {
        104 this.customCommands_[name] = {method: method, path: path};
        105};
        106
        107
        108/** @override */
        109webdriver.http.Executor.prototype.execute = function(command, callback) {
        110 var resource =
        111 this.customCommands_[command.getName()] ||
        112 webdriver.http.Executor.COMMAND_MAP_[command.getName()];
        113 if (!resource) {
        114 throw new Error('Unrecognized command: ' + command.getName());
        115 }
        116
        117 var parameters = command.getParameters();
        118 var path = webdriver.http.Executor.buildPath_(resource.path, parameters);
        119 var request = new webdriver.http.Request(resource.method, path, parameters);
        120
        121 var log = this.log_;
        122 log.finer(function() {
        123 return '>>>\n' + request;
        124 });
        125
        126 this.client_.send(request, function(e, response) {
        127 var responseObj;
        128 if (!e) {
        129 log.finer(function() {
        130 return '<<<\n' + response;
        131 });
        132 try {
        133 responseObj = webdriver.http.Executor.parseHttpResponse_(
        134 /** @type {!webdriver.http.Response} */ (response));
        135 } catch (ex) {
        136 log.warning('Error parsing response', ex);
        137 e = ex;
        138 }
        139 }
        140 callback(e, responseObj);
        141 });
        142};
        143
        144
        145/**
        146 * Builds a fully qualified path using the given set of command parameters. Each
        147 * path segment prefixed with ':' will be replaced by the value of the
        148 * corresponding parameter. All parameters spliced into the path will be
        149 * removed from the parameter map.
        150 * @param {string} path The original resource path.
        151 * @param {!Object.<*>} parameters The parameters object to splice into
        152 * the path.
        153 * @return {string} The modified path.
        154 * @private
        155 */
        156webdriver.http.Executor.buildPath_ = function(path, parameters) {
        157 var pathParameters = path.match(/\/:(\w+)\b/g);
        158 if (pathParameters) {
        159 for (var i = 0; i < pathParameters.length; ++i) {
        160 var key = pathParameters[i].substring(2); // Trim the /:
        161 if (key in parameters) {
        162 var value = parameters[key];
        163 // TODO: move webdriver.WebElement.ELEMENT definition to a
        164 // common file so we can reference it here without pulling in all of
        165 // webdriver.WebElement's dependencies.
        166 if (value && value['ELEMENT']) {
        167 // When inserting a WebElement into the URL, only use its ID value,
        168 // not the full JSON.
        169 value = value['ELEMENT'];
        170 }
        171 path = path.replace(pathParameters[i], '/' + value);
        172 delete parameters[key];
        173 } else {
        174 throw new Error('Missing required parameter: ' + key);
        175 }
        176 }
        177 }
        178 return path;
        179};
        180
        181
        182/**
        183 * Callback used to parse {@link webdriver.http.Response} objects from a
        184 * {@link webdriver.http.Client}.
        185 * @param {!webdriver.http.Response} httpResponse The HTTP response to parse.
        186 * @return {!bot.response.ResponseObject} The parsed response.
        187 * @private
        188 */
        189webdriver.http.Executor.parseHttpResponse_ = function(httpResponse) {
        190 try {
        191 return /** @type {!bot.response.ResponseObject} */ (JSON.parse(
        192 httpResponse.body));
        193 } catch (ex) {
        194 // Whoops, looks like the server sent us a malformed response. We'll need
        195 // to manually build a response object based on the response code.
        196 }
        197
        198 var response = {
        199 'status': bot.ErrorCode.SUCCESS,
        200 'value': httpResponse.body.replace(/\r\n/g, '\n')
        201 };
        202
        203 if (!(httpResponse.status > 199 && httpResponse.status < 300)) {
        204 // 404 represents an unknown command; anything else is a generic unknown
        205 // error.
        206 response['status'] = httpResponse.status == 404 ?
        207 bot.ErrorCode.UNKNOWN_COMMAND :
        208 bot.ErrorCode.UNKNOWN_ERROR;
        209 }
        210
        211 return response;
        212};
        213
        214
        215/**
        216 * Maps command names to resource locator.
        217 * @private {!Object.<{method:string, path:string}>}
        218 * @const
        219 */
        220webdriver.http.Executor.COMMAND_MAP_ = (function() {
        221 return new Builder().
        222 put(webdriver.CommandName.GET_SERVER_STATUS, get('/status')).
        223 put(webdriver.CommandName.NEW_SESSION, post('/session')).
        224 put(webdriver.CommandName.GET_SESSIONS, get('/sessions')).
        225 put(webdriver.CommandName.DESCRIBE_SESSION, get('/session/:sessionId')).
        226 put(webdriver.CommandName.QUIT, del('/session/:sessionId')).
        227 put(webdriver.CommandName.CLOSE, del('/session/:sessionId/window')).
        228 put(webdriver.CommandName.GET_CURRENT_WINDOW_HANDLE,
        229 get('/session/:sessionId/window_handle')).
        230 put(webdriver.CommandName.GET_WINDOW_HANDLES,
        231 get('/session/:sessionId/window_handles')).
        232 put(webdriver.CommandName.GET_CURRENT_URL,
        233 get('/session/:sessionId/url')).
        234 put(webdriver.CommandName.GET, post('/session/:sessionId/url')).
        235 put(webdriver.CommandName.GO_BACK, post('/session/:sessionId/back')).
        236 put(webdriver.CommandName.GO_FORWARD,
        237 post('/session/:sessionId/forward')).
        238 put(webdriver.CommandName.REFRESH,
        239 post('/session/:sessionId/refresh')).
        240 put(webdriver.CommandName.ADD_COOKIE,
        241 post('/session/:sessionId/cookie')).
        242 put(webdriver.CommandName.GET_ALL_COOKIES,
        243 get('/session/:sessionId/cookie')).
        244 put(webdriver.CommandName.DELETE_ALL_COOKIES,
        245 del('/session/:sessionId/cookie')).
        246 put(webdriver.CommandName.DELETE_COOKIE,
        247 del('/session/:sessionId/cookie/:name')).
        248 put(webdriver.CommandName.FIND_ELEMENT,
        249 post('/session/:sessionId/element')).
        250 put(webdriver.CommandName.FIND_ELEMENTS,
        251 post('/session/:sessionId/elements')).
        252 put(webdriver.CommandName.GET_ACTIVE_ELEMENT,
        253 post('/session/:sessionId/element/active')).
        254 put(webdriver.CommandName.FIND_CHILD_ELEMENT,
        255 post('/session/:sessionId/element/:id/element')).
        256 put(webdriver.CommandName.FIND_CHILD_ELEMENTS,
        257 post('/session/:sessionId/element/:id/elements')).
        258 put(webdriver.CommandName.CLEAR_ELEMENT,
        259 post('/session/:sessionId/element/:id/clear')).
        260 put(webdriver.CommandName.CLICK_ELEMENT,
        261 post('/session/:sessionId/element/:id/click')).
        262 put(webdriver.CommandName.SEND_KEYS_TO_ELEMENT,
        263 post('/session/:sessionId/element/:id/value')).
        264 put(webdriver.CommandName.SUBMIT_ELEMENT,
        265 post('/session/:sessionId/element/:id/submit')).
        266 put(webdriver.CommandName.GET_ELEMENT_TEXT,
        267 get('/session/:sessionId/element/:id/text')).
        268 put(webdriver.CommandName.GET_ELEMENT_TAG_NAME,
        269 get('/session/:sessionId/element/:id/name')).
        270 put(webdriver.CommandName.IS_ELEMENT_SELECTED,
        271 get('/session/:sessionId/element/:id/selected')).
        272 put(webdriver.CommandName.IS_ELEMENT_ENABLED,
        273 get('/session/:sessionId/element/:id/enabled')).
        274 put(webdriver.CommandName.IS_ELEMENT_DISPLAYED,
        275 get('/session/:sessionId/element/:id/displayed')).
        276 put(webdriver.CommandName.GET_ELEMENT_LOCATION,
        277 get('/session/:sessionId/element/:id/location')).
        278 put(webdriver.CommandName.GET_ELEMENT_SIZE,
        279 get('/session/:sessionId/element/:id/size')).
        280 put(webdriver.CommandName.GET_ELEMENT_ATTRIBUTE,
        281 get('/session/:sessionId/element/:id/attribute/:name')).
        282 put(webdriver.CommandName.GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
        283 get('/session/:sessionId/element/:id/css/:propertyName')).
        284 put(webdriver.CommandName.ELEMENT_EQUALS,
        285 get('/session/:sessionId/element/:id/equals/:other')).
        286 put(webdriver.CommandName.SWITCH_TO_WINDOW,
        287 post('/session/:sessionId/window')).
        288 put(webdriver.CommandName.MAXIMIZE_WINDOW,
        289 post('/session/:sessionId/window/:windowHandle/maximize')).
        290 put(webdriver.CommandName.GET_WINDOW_POSITION,
        291 get('/session/:sessionId/window/:windowHandle/position')).
        292 put(webdriver.CommandName.SET_WINDOW_POSITION,
        293 post('/session/:sessionId/window/:windowHandle/position')).
        294 put(webdriver.CommandName.GET_WINDOW_SIZE,
        295 get('/session/:sessionId/window/:windowHandle/size')).
        296 put(webdriver.CommandName.SET_WINDOW_SIZE,
        297 post('/session/:sessionId/window/:windowHandle/size')).
        298 put(webdriver.CommandName.SWITCH_TO_FRAME,
        299 post('/session/:sessionId/frame')).
        300 put(webdriver.CommandName.GET_PAGE_SOURCE,
        301 get('/session/:sessionId/source')).
        302 put(webdriver.CommandName.GET_TITLE,
        303 get('/session/:sessionId/title')).
        304 put(webdriver.CommandName.EXECUTE_SCRIPT,
        305 post('/session/:sessionId/execute')).
        306 put(webdriver.CommandName.EXECUTE_ASYNC_SCRIPT,
        307 post('/session/:sessionId/execute_async')).
        308 put(webdriver.CommandName.SCREENSHOT,
        309 get('/session/:sessionId/screenshot')).
        310 put(webdriver.CommandName.SET_TIMEOUT,
        311 post('/session/:sessionId/timeouts')).
        312 put(webdriver.CommandName.SET_SCRIPT_TIMEOUT,
        313 post('/session/:sessionId/timeouts/async_script')).
        314 put(webdriver.CommandName.IMPLICITLY_WAIT,
        315 post('/session/:sessionId/timeouts/implicit_wait')).
        316 put(webdriver.CommandName.MOVE_TO, post('/session/:sessionId/moveto')).
        317 put(webdriver.CommandName.CLICK, post('/session/:sessionId/click')).
        318 put(webdriver.CommandName.DOUBLE_CLICK,
        319 post('/session/:sessionId/doubleclick')).
        320 put(webdriver.CommandName.MOUSE_DOWN,
        321 post('/session/:sessionId/buttondown')).
        322 put(webdriver.CommandName.MOUSE_UP, post('/session/:sessionId/buttonup')).
        323 put(webdriver.CommandName.MOVE_TO, post('/session/:sessionId/moveto')).
        324 put(webdriver.CommandName.SEND_KEYS_TO_ACTIVE_ELEMENT,
        325 post('/session/:sessionId/keys')).
        326 put(webdriver.CommandName.TOUCH_SINGLE_TAP,
        327 post('/session/:sessionId/touch/click')).
        328 put(webdriver.CommandName.TOUCH_DOUBLE_TAP,
        329 post('/session/:sessionId/touch/doubleclick')).
        330 put(webdriver.CommandName.TOUCH_DOWN,
        331 post('/session/:sessionId/touch/down')).
        332 put(webdriver.CommandName.TOUCH_UP,
        333 post('/session/:sessionId/touch/up')).
        334 put(webdriver.CommandName.TOUCH_MOVE,
        335 post('/session/:sessionId/touch/move')).
        336 put(webdriver.CommandName.TOUCH_SCROLL,
        337 post('/session/:sessionId/touch/scroll')).
        338 put(webdriver.CommandName.TOUCH_LONG_PRESS,
        339 post('/session/:sessionId/touch/longclick')).
        340 put(webdriver.CommandName.TOUCH_FLICK,
        341 post('/session/:sessionId/touch/flick')).
        342 put(webdriver.CommandName.ACCEPT_ALERT,
        343 post('/session/:sessionId/accept_alert')).
        344 put(webdriver.CommandName.DISMISS_ALERT,
        345 post('/session/:sessionId/dismiss_alert')).
        346 put(webdriver.CommandName.GET_ALERT_TEXT,
        347 get('/session/:sessionId/alert_text')).
        348 put(webdriver.CommandName.SET_ALERT_TEXT,
        349 post('/session/:sessionId/alert_text')).
        350 put(webdriver.CommandName.GET_LOG, post('/session/:sessionId/log')).
        351 put(webdriver.CommandName.GET_AVAILABLE_LOG_TYPES,
        352 get('/session/:sessionId/log/types')).
        353 put(webdriver.CommandName.GET_SESSION_LOGS, post('/logs')).
        354 put(webdriver.CommandName.UPLOAD_FILE, post('/session/:sessionId/file')).
        355 build();
        356
        357 /** @constructor */
        358 function Builder() {
        359 var map = {};
        360
        361 this.put = function(name, resource) {
        362 map[name] = resource;
        363 return this;
        364 };
        365
        366 this.build = function() {
        367 return map;
        368 };
        369 }
        370
        371 function post(path) { return resource('POST', path); }
        372 function del(path) { return resource('DELETE', path); }
        373 function get(path) { return resource('GET', path); }
        374 function resource(method, path) { return {method: method, path: path}; }
        375})();
        376
        377
        378/**
        379 * Converts a headers object to a HTTP header block string.
        380 * @param {!Object.<string>} headers The headers object to convert.
        381 * @return {string} The headers as a string.
        382 * @private
        383 */
        384webdriver.http.headersToString_ = function(headers) {
        385 var ret = [];
        386 for (var key in headers) {
        387 ret.push(key + ': ' + headers[key]);
        388 }
        389 return ret.join('\n');
        390};
        391
        392
        393
        394/**
        395 * Describes a partial HTTP request. This class is a "partial" request and only
        396 * defines the path on the server to send a request to. It is each
        397 * {@link webdriver.http.Client}'s responsibility to build the full URL for the
        398 * final request.
        399 * @param {string} method The HTTP method to use for the request.
        400 * @param {string} path Path on the server to send the request to.
        401 * @param {Object=} opt_data This request's JSON data.
        402 * @constructor
        403 */
        404webdriver.http.Request = function(method, path, opt_data) {
        405
        406 /**
        407 * The HTTP method to use for the request.
        408 * @type {string}
        409 */
        410 this.method = method;
        411
        412 /**
        413 * The path on the server to send the request to.
        414 * @type {string}
        415 */
        416 this.path = path;
        417
        418 /**
        419 * This request's body.
        420 * @type {!Object}
        421 */
        422 this.data = opt_data || {};
        423
        424 /**
        425 * The headers to send with the request.
        426 * @type {!Object.<(string|number)>}
        427 */
        428 this.headers = {'Accept': 'application/json; charset=utf-8'};
        429};
        430
        431
        432/** @override */
        433webdriver.http.Request.prototype.toString = function() {
        434 return [
        435 this.method + ' ' + this.path + ' HTTP/1.1',
        436 webdriver.http.headersToString_(this.headers),
        437 '',
        438 JSON.stringify(this.data)
        439 ].join('\n');
        440};
        441
        442
        443
        444/**
        445 * Represents a HTTP response.
        446 * @param {number} status The response code.
        447 * @param {!Object.<string>} headers The response headers. All header
        448 * names will be converted to lowercase strings for consistent lookups.
        449 * @param {string} body The response body.
        450 * @constructor
        451 */
        452webdriver.http.Response = function(status, headers, body) {
        453
        454 /**
        455 * The HTTP response code.
        456 * @type {number}
        457 */
        458 this.status = status;
        459
        460 /**
        461 * The response body.
        462 * @type {string}
        463 */
        464 this.body = body;
        465
        466 /**
        467 * The response body.
        468 * @type {!Object.<string>}
        469 */
        470 this.headers = {};
        471 for (var header in headers) {
        472 this.headers[header.toLowerCase()] = headers[header];
        473 }
        474};
        475
        476
        477/**
        478 * Builds a {@link webdriver.http.Response} from a {@link XMLHttpRequest} or
        479 * {@link XDomainRequest} response object.
        480 * @param {!(XDomainRequest|XMLHttpRequest)} xhr The request to parse.
        481 * @return {!webdriver.http.Response} The parsed response.
        482 */
        483webdriver.http.Response.fromXmlHttpRequest = function(xhr) {
        484 var headers = {};
        485
        486 // getAllResponseHeaders is only available on XMLHttpRequest objects.
        487 if (xhr.getAllResponseHeaders) {
        488 var tmp = xhr.getAllResponseHeaders();
        489 if (tmp) {
        490 tmp = tmp.replace(/\r\n/g, '\n').split('\n');
        491 goog.array.forEach(tmp, function(header) {
        492 var parts = header.split(/\s*:\s*/, 2);
        493 if (parts[0]) {
        494 headers[parts[0]] = parts[1] || '';
        495 }
        496 });
        497 }
        498 }
        499
        500 // If xhr is a XDomainRequest object, it will not have a status.
        501 // However, if we're parsing the response from a XDomainRequest, then
        502 // that request must have been a success, so we can assume status == 200.
        503 var status = xhr.status || 200;
        504 return new webdriver.http.Response(status, headers,
        505 xhr.responseText.replace(/\0/g, ''));
        506};
        507
        508
        509/** @override */
        510webdriver.http.Response.prototype.toString = function() {
        511 var headers = webdriver.http.headersToString_(this.headers);
        512 var ret = ['HTTP/1.1 ' + this.status, headers];
        513
        514 if (headers) {
        515 ret.push('');
        516 }
        517
        518 if (this.body) {
        519 ret.push(this.body);
        520 }
        521
        522 return ret.join('\n');
        523};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/http/xhrclient.js.src.html b/docs/source/lib/webdriver/http/xhrclient.js.src.html index d810afe..a71f98f 100644 --- a/docs/source/lib/webdriver/http/xhrclient.js.src.html +++ b/docs/source/lib/webdriver/http/xhrclient.js.src.html @@ -1 +1 @@ -xhrclient.js

        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
        17goog.provide('webdriver.http.XhrClient');
        18
        19goog.require('goog.json');
        20goog.require('goog.net.XmlHttp');
        21goog.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 */
        31webdriver.http.XhrClient = function(url) {
        32
        33 /** @private {string} */
        34 this.url_ = url;
        35};
        36
        37
        38/** @override */
        39webdriver.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};
        \ No newline at end of file +xhrclient.js

        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
        17goog.provide('webdriver.http.XhrClient');
        18
        19goog.require('goog.json');
        20goog.require('goog.net.XmlHttp');
        21goog.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 */
        31webdriver.http.XhrClient = function(url) {
        32
        33 /** @private {string} */
        34 this.url_ = url;
        35};
        36
        37
        38/** @override */
        39webdriver.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};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/key.js.src.html b/docs/source/lib/webdriver/key.js.src.html index 4cb50ac..1813550 100644 --- a/docs/source/lib/webdriver/key.js.src.html +++ b/docs/source/lib/webdriver/key.js.src.html @@ -1 +1 @@ -key.js

        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
        15goog.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 */
        25webdriver.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};
        \ No newline at end of file +key.js

        lib/webdriver/key.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18goog.provide('webdriver.Key');
        19
        20
        21/**
        22 * Representations of pressable keys that aren't text. These are stored in
        23 * the Unicode PUA (Private Use Area) code points, 0xE000-0xF8FF. Refer to
        24 * http://www.google.com.au/search?&q=unicode+pua&btnG=Search
        25 *
        26 * @enum {string}
        27 */
        28webdriver.Key = {
        29 NULL: '\uE000',
        30 CANCEL: '\uE001', // ^break
        31 HELP: '\uE002',
        32 BACK_SPACE: '\uE003',
        33 TAB: '\uE004',
        34 CLEAR: '\uE005',
        35 RETURN: '\uE006',
        36 ENTER: '\uE007',
        37 SHIFT: '\uE008',
        38 CONTROL: '\uE009',
        39 ALT: '\uE00A',
        40 PAUSE: '\uE00B',
        41 ESCAPE: '\uE00C',
        42 SPACE: '\uE00D',
        43 PAGE_UP: '\uE00E',
        44 PAGE_DOWN: '\uE00F',
        45 END: '\uE010',
        46 HOME: '\uE011',
        47 ARROW_LEFT: '\uE012',
        48 LEFT: '\uE012',
        49 ARROW_UP: '\uE013',
        50 UP: '\uE013',
        51 ARROW_RIGHT: '\uE014',
        52 RIGHT: '\uE014',
        53 ARROW_DOWN: '\uE015',
        54 DOWN: '\uE015',
        55 INSERT: '\uE016',
        56 DELETE: '\uE017',
        57 SEMICOLON: '\uE018',
        58 EQUALS: '\uE019',
        59
        60 NUMPAD0: '\uE01A', // number pad keys
        61 NUMPAD1: '\uE01B',
        62 NUMPAD2: '\uE01C',
        63 NUMPAD3: '\uE01D',
        64 NUMPAD4: '\uE01E',
        65 NUMPAD5: '\uE01F',
        66 NUMPAD6: '\uE020',
        67 NUMPAD7: '\uE021',
        68 NUMPAD8: '\uE022',
        69 NUMPAD9: '\uE023',
        70 MULTIPLY: '\uE024',
        71 ADD: '\uE025',
        72 SEPARATOR: '\uE026',
        73 SUBTRACT: '\uE027',
        74 DECIMAL: '\uE028',
        75 DIVIDE: '\uE029',
        76
        77 F1: '\uE031', // function keys
        78 F2: '\uE032',
        79 F3: '\uE033',
        80 F4: '\uE034',
        81 F5: '\uE035',
        82 F6: '\uE036',
        83 F7: '\uE037',
        84 F8: '\uE038',
        85 F9: '\uE039',
        86 F10: '\uE03A',
        87 F11: '\uE03B',
        88 F12: '\uE03C',
        89
        90 COMMAND: '\uE03D', // Apple command key
        91 META: '\uE03D' // alias for Windows key
        92};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/locators.js.src.html b/docs/source/lib/webdriver/locators.js.src.html index 6587c12..8cedcb3 100644 --- a/docs/source/lib/webdriver/locators.js.src.html +++ b/docs/source/lib/webdriver/locators.js.src.html @@ -1 +1 @@ -locators.js

        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
        19goog.provide('webdriver.By');
        20goog.provide('webdriver.Locator');
        21goog.provide('webdriver.Locator.Strategy');
        22
        23goog.require('goog.array');
        24goog.require('goog.object');
        25goog.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 */
        35webdriver.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 */
        57webdriver.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 */
        68webdriver.By = {};
        69// Exported to the global scope for legacy reasons.
        70goog.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 */
        96webdriver.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 */
        108webdriver.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 */
        121webdriver.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 */
        130webdriver.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 */
        140webdriver.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 */
        153webdriver.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 */
        167webdriver.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 */
        177webdriver.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 */
        189webdriver.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 */
        204webdriver.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 */
        212webdriver.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 */
        233webdriver.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 */
        249webdriver.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};
        \ No newline at end of file +locators.js

        lib/webdriver/locators.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Factory methods for the supported locator strategies.
        20 */
        21
        22goog.provide('webdriver.By');
        23goog.provide('webdriver.Locator');
        24goog.provide('webdriver.Locator.Strategy');
        25
        26goog.require('goog.array');
        27goog.require('goog.object');
        28goog.require('goog.string');
        29
        30
        31
        32/**
        33 * An element locator.
        34 * @param {string} using The type of strategy to use for this locator.
        35 * @param {string} value The search target of this locator.
        36 * @constructor
        37 */
        38webdriver.Locator = function(using, value) {
        39
        40 /**
        41 * The search strategy to use when searching for an element.
        42 * @type {string}
        43 */
        44 this.using = using;
        45
        46 /**
        47 * The search target for this locator.
        48 * @type {string}
        49 */
        50 this.value = value;
        51};
        52
        53
        54/**
        55 * Creates a factory function for a {@link webdriver.Locator}.
        56 * @param {string} type The type of locator for the factory.
        57 * @return {function(string): !webdriver.Locator} The new factory function.
        58 * @private
        59 */
        60webdriver.Locator.factory_ = function(type) {
        61 return function(value) {
        62 return new webdriver.Locator(type, value);
        63 };
        64};
        65
        66
        67/**
        68 * A collection of factory functions for creating {@link webdriver.Locator}
        69 * instances.
        70 */
        71webdriver.By = {};
        72// Exported to the global scope for legacy reasons.
        73goog.exportSymbol('By', webdriver.By);
        74
        75
        76/**
        77 * Short-hand expressions for the primary element locator strategies.
        78 * For example the following two statements are equivalent:
        79 *
        80 * var e1 = driver.findElement(webdriver.By.id('foo'));
        81 * var e2 = driver.findElement({id: 'foo'});
        82 *
        83 * Care should be taken when using JavaScript minifiers (such as the
        84 * Closure compiler), as locator hashes will always be parsed using
        85 * the un-obfuscated properties listed.
        86 *
        87 * @typedef {(
        88 * {className: string}|
        89 * {css: string}|
        90 * {id: string}|
        91 * {js: string}|
        92 * {linkText: string}|
        93 * {name: string}|
        94 * {partialLinkText: string}|
        95 * {tagName: string}|
        96 * {xpath: string})}
        97 */
        98webdriver.By.Hash;
        99
        100
        101/**
        102 * Locates elements that have a specific class name. The returned locator
        103 * is equivalent to searching for elements with the CSS selector ".clazz".
        104 *
        105 * @param {string} className The class name to search for.
        106 * @return {!webdriver.Locator} The new locator.
        107 * @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
        108 * @see http://www.w3.org/TR/CSS2/selector.html#class-html
        109 */
        110webdriver.By.className = webdriver.Locator.factory_('class name');
        111
        112
        113/**
        114 * Locates elements using a CSS selector. For browsers that do not support
        115 * CSS selectors, WebDriver implementations may return an
        116 * {@linkplain bot.Error.State.INVALID_SELECTOR invalid selector} error. An
        117 * implementation may, however, emulate the CSS selector API.
        118 *
        119 * @param {string} selector The CSS selector to use.
        120 * @return {!webdriver.Locator} The new locator.
        121 * @see http://www.w3.org/TR/CSS2/selector.html
        122 */
        123webdriver.By.css = webdriver.Locator.factory_('css selector');
        124
        125
        126/**
        127 * Locates an element by its ID.
        128 *
        129 * @param {string} id The ID to search for.
        130 * @return {!webdriver.Locator} The new locator.
        131 */
        132webdriver.By.id = webdriver.Locator.factory_('id');
        133
        134
        135/**
        136 * Locates link elements whose {@linkplain webdriver.WebElement#getText visible
        137 * text} matches the given string.
        138 *
        139 * @param {string} text The link text to search for.
        140 * @return {!webdriver.Locator} The new locator.
        141 */
        142webdriver.By.linkText = webdriver.Locator.factory_('link text');
        143
        144
        145/**
        146 * Locates an elements by evaluating a
        147 * {@linkplain webdriver.WebDriver#executeScript JavaScript expression}.
        148 * The result of this expression must be an element or list of elements.
        149 *
        150 * @param {!(string|Function)} script The script to execute.
        151 * @param {...*} var_args The arguments to pass to the script.
        152 * @return {function(!webdriver.WebDriver): !webdriver.promise.Promise} A new,
        153 * JavaScript-based locator function.
        154 */
        155webdriver.By.js = function(script, var_args) {
        156 var args = goog.array.slice(arguments, 0);
        157 return function(driver) {
        158 return driver.executeScript.apply(driver, args);
        159 };
        160};
        161
        162
        163/**
        164 * Locates elements whose {@code name} attribute has the given value.
        165 *
        166 * @param {string} name The name attribute to search for.
        167 * @return {!webdriver.Locator} The new locator.
        168 */
        169webdriver.By.name = webdriver.Locator.factory_('name');
        170
        171
        172/**
        173 * Locates link elements whose {@linkplain webdriver.WebElement#getText visible
        174 * text} contains the given substring.
        175 *
        176 * @param {string} text The substring to check for in a link's visible text.
        177 * @return {!webdriver.Locator} The new locator.
        178 */
        179webdriver.By.partialLinkText = webdriver.Locator.factory_(
        180 'partial link text');
        181
        182
        183/**
        184 * Locates elements with a given tag name. The returned locator is
        185 * equivalent to using the
        186 * [getElementsByTagName](https://developer.mozilla.org/en-US/docs/Web/API/Element.getElementsByTagName)
        187 * DOM function.
        188 *
        189 * @param {string} text The substring to check for in a link's visible text.
        190 * @return {!webdriver.Locator} The new locator.
        191 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html
        192 */
        193webdriver.By.tagName = webdriver.Locator.factory_('tag name');
        194
        195
        196/**
        197 * Locates elements matching a XPath selector. Care should be taken when
        198 * using an XPath selector with a {@link webdriver.WebElement} as WebDriver
        199 * will respect the context in the specified in the selector. For example,
        200 * given the selector {@code "//div"}, WebDriver will search from the
        201 * document root regardless of whether the locator was used with a
        202 * WebElement.
        203 *
        204 * @param {string} xpath The XPath selector to use.
        205 * @return {!webdriver.Locator} The new locator.
        206 * @see http://www.w3.org/TR/xpath/
        207 */
        208webdriver.By.xpath = webdriver.Locator.factory_('xpath');
        209
        210
        211/**
        212 * Maps {@link webdriver.By.Hash} keys to the appropriate factory function.
        213 * @type {!Object.<string, function(string): !(Function|webdriver.Locator)>}
        214 * @const
        215 */
        216webdriver.Locator.Strategy = {
        217 'className': webdriver.By.className,
        218 'css': webdriver.By.css,
        219 'id': webdriver.By.id,
        220 'js': webdriver.By.js,
        221 'linkText': webdriver.By.linkText,
        222 'name': webdriver.By.name,
        223 'partialLinkText': webdriver.By.partialLinkText,
        224 'tagName': webdriver.By.tagName,
        225 'xpath': webdriver.By.xpath
        226};
        227
        228
        229/**
        230 * Verifies that a {@code value} is a valid locator to use for searching for
        231 * elements on the page.
        232 *
        233 * @param {*} value The value to check is a valid locator.
        234 * @return {!(webdriver.Locator|Function)} A valid locator object or function.
        235 * @throws {TypeError} If the given value is an invalid locator.
        236 */
        237webdriver.Locator.checkLocator = function(value) {
        238 if (goog.isFunction(value) || value instanceof webdriver.Locator) {
        239 return value;
        240 }
        241 for (var key in value) {
        242 if (value.hasOwnProperty(key) &&
        243 webdriver.Locator.Strategy.hasOwnProperty(key)) {
        244 return webdriver.Locator.Strategy[key](value[key]);
        245 }
        246 }
        247 throw new TypeError('Invalid locator');
        248};
        249
        250
        251
        252/** @override */
        253webdriver.Locator.prototype.toString = function() {
        254 return 'By.' + this.using.replace(/ ([a-z])/g, function(all, match) {
        255 return match.toUpperCase();
        256 }) + '(' + goog.string.quote(this.value) + ')';
        257};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/logging.js.src.html b/docs/source/lib/webdriver/logging.js.src.html index b9153c0..6d4709c 100644 --- a/docs/source/lib/webdriver/logging.js.src.html +++ b/docs/source/lib/webdriver/logging.js.src.html @@ -1 +1 @@ -logging.js

        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
        16goog.provide('webdriver.logging');
        17goog.provide('webdriver.logging.Preferences');
        18
        19goog.require('goog.object');
        20
        21
        22/**
        23 * Log level names from WebDriver's JSON wire protocol.
        24 * @enum {string}
        25 */
        26webdriver.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 */
        40webdriver.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 */
        58webdriver.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 */
        72webdriver.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 */
        90webdriver.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 */
        103webdriver.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 */
        124webdriver.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 */
        141webdriver.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};
        \ No newline at end of file +logging.js

        lib/webdriver/logging.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines WebDriver's logging system. The logging system is
        20 * broken into major components: local and remote logging.
        21 *
        22 * The local logging API, which is anchored by the
        23 * {@link webdriver.logging.Logger Logger} class, is similar to Java's
        24 * logging API. Loggers, retrieved by {@link webdriver.logging.getLogger}, use
        25 * hierarchical, dot-delimited namespaces
        26 * (e.g. "" > "webdriver" > "webdriver.logging"). Recorded log messages are
        27 * represented by the {@link webdriver.logging.LogRecord LogRecord} class. You
        28 * can capture log records by
        29 * {@linkplain webdriver.logging.Logger#addHandler attaching} a handler function
        30 * to the desired logger. For convenience, you can quickly enable logging to
        31 * the console by simply calling
        32 * {@link webdriver.logging.installConsoleHandler()}.
        33 *
        34 * The [remote logging API](https://github.com/SeleniumHQ/selenium/wiki/Logging)
        35 * allows you to retrieve logs from a remote WebDriver server. This API uses the
        36 * {@link Preferences} class to define desired log levels prior to create a
        37 * WebDriver session:
        38 *
        39 * var prefs = new webdriver.logging.Preferences();
        40 * prefs.setLevel(webdriver.logging.Type.BROWSER,
        41 * webdriver.logging.Level.DEBUG);
        42 *
        43 * var caps = webdriver.Capabilities.chrome();
        44 * caps.setLoggingPrefs(prefs);
        45 * // ...
        46 *
        47 * Remote log entries are represented by the {@link Entry} class and may be
        48 * retrieved via {@link webdriver.WebDriver.Logs}:
        49 *
        50 * driver.manage().logs().get(webdriver.logging.Type.BROWSER)
        51 * .then(function(entries) {
        52 * entries.forEach(function(entry) {
        53 * console.log('[%s] %s', entry.level.name, entry.message);
        54 * });
        55 * });
        56 *
        57 * **NOTE:** Only a few browsers support the remote logging API (notably
        58 * Firefox and Chrome). Firefox supports basic logging functionality, while
        59 * Chrome exposes robust
        60 * [performance logging](https://sites.google.com/a/chromium.org/chromedriver/logging)
        61 * options. Remote logging is still considered a non-standard feature, and the
        62 * APIs exposed by this module for it are non-frozen. Once logging is officially
        63 * defined by the [W3C WebDriver spec](http://www.w3.org/TR/webdriver/), this
        64 * module will be updated to use a consistent API for local and remote logging.
        65 */
        66
        67goog.module('webdriver.logging');
        68goog.module.declareLegacyNamespace();
        69
        70var LogManager = goog.require('goog.debug.LogManager');
        71var LogRecord = goog.require('goog.debug.LogRecord');
        72var Logger = goog.require('goog.debug.Logger');
        73var Objects = goog.require('goog.object');
        74var padNumber = goog.require('goog.string').padNumber;
        75
        76
        77/** @const */
        78exports.LogRecord = LogRecord;
        79
        80
        81/** @const */
        82exports.Logger = Logger;
        83
        84
        85/** @const */
        86exports.Level = Logger.Level;
        87
        88
        89/**
        90 * DEBUG is a message level for debugging messages and has the same log level
        91 * as the {@link Logger.Level.CONFIG} message level.
        92 * @const {!Logger.Level}
        93 */
        94Logger.Level.DEBUG = new Logger.Level('DEBUG', Logger.Level.CONFIG.value);
        95
        96
        97/**
        98 * Finds a named logger.
        99 *
        100 * @param {string=} opt_name The dot-delimited logger name, such as
        101 * "webdriver.logging.Logger". Defaults to the name of the root logger.
        102 * @return {!Logger} The named logger.
        103 */
        104function getLogger(opt_name) {
        105 return LogManager.getLogger(opt_name || Logger.ROOT_LOGGER_NAME);
        106}
        107exports.getLogger = getLogger;
        108
        109
        110/**
        111 * Logs all messages to the Console API.
        112 */
        113function consoleHandler(record) {
        114 if (typeof console === 'undefined' || !console) {
        115 return;
        116 }
        117 record = /** @type {!LogRecord} */(record);
        118 var timestamp = new Date(record.getMillis());
        119 var msg =
        120 '[' + timestamp.getUTCFullYear() + '-' +
        121 padNumber(timestamp.getUTCMonth() + 1, 2) + '-' +
        122 padNumber(timestamp.getUTCDate(), 2) + 'T' +
        123 padNumber(timestamp.getUTCHours(), 2) + ':' +
        124 padNumber(timestamp.getUTCMinutes(), 2) + ':' +
        125 padNumber(timestamp.getUTCSeconds(), 2) + 'Z]' +
        126 '[' + record.getLevel().name + ']' +
        127 '[' + record.getLoggerName() + '] ' +
        128 record.getMessage();
        129
        130 var level = record.getLevel().value;
        131 if (level >= Logger.Level.SEVERE.value) {
        132 console.error(msg);
        133 } else if (level >= Logger.Level.WARNING.value) {
        134 console.warn(msg);
        135 } else {
        136 console.log(msg);
        137 }
        138}
        139
        140
        141/**
        142 * Adds the console handler to the given logger. The console handler will log
        143 * all messages using the JavaScript Console API.
        144 *
        145 * @param {Logger=} opt_logger The logger to add the handler to; defaults
        146 * to the root logger.
        147 * @see exports.removeConsoleHandler
        148 */
        149exports.addConsoleHandler = function(opt_logger) {
        150 var logger = opt_logger || getLogger();
        151 logger.addHandler(consoleHandler);
        152};
        153
        154
        155/**
        156 * Installs the console log handler on the root logger.
        157 * @see exports.addConsoleHandler
        158 */
        159exports.installConsoleHandler = function() {
        160 exports.addConsoleHandler();
        161};
        162
        163
        164/**
        165 * Removes the console log handler from the given logger.
        166 *
        167 * @param {Logger=} opt_logger The logger to remove the handler from; defaults
        168 * to the root logger.
        169 * @see exports.addConsoleHandler
        170 */
        171exports.removeConsoleHandler = function(opt_logger) {
        172 var logger = opt_logger || getLogger();
        173 logger.removeHandler(consoleHandler);
        174};
        175
        176
        177/**
        178 * Converts a level name or value to a {@link webdriver.logging.Level} value.
        179 * If the name/value is not recognized, {@link webdriver.logging.Level.ALL}
        180 * will be returned.
        181 * @param {(number|string)} nameOrValue The log level name, or value, to
        182 * convert .
        183 * @return {!Logger.Level} The converted level.
        184 */
        185function getLevel(nameOrValue) {
        186 // DEBUG is not a predefined Closure log level, but maps to CONFIG. Since
        187 // DEBUG is a predefined level for the WebDriver protocol, we prefer it over
        188 // CONFIG.
        189 if ('DEBUG' === nameOrValue || Logger.Level.DEBUG.value === nameOrValue) {
        190 return Logger.Level.DEBUG;
        191 } else if (goog.isString(nameOrValue)) {
        192 return Logger.Level.getPredefinedLevel(/** @type {string} */(nameOrValue))
        193 || Logger.Level.ALL;
        194 } else {
        195 return Logger.Level.getPredefinedLevelByValue(
        196 /** @type {number} */(nameOrValue)) || Logger.Level.ALL;
        197 }
        198}
        199exports.getLevel = getLevel;
        200
        201
        202/**
        203 * Normalizes a {@link Logger.Level} to one of the distinct values recognized
        204 * by WebDriver's wire protocol.
        205 * @param {!Logger.Level} level The input level.
        206 * @return {!Logger.Level} The normalized level.
        207 */
        208function normalizeLevel(level) {
        209 if (level.value <= Logger.Level.ALL.value) { // ALL is 0.
        210 return Logger.Level.ALL;
        211
        212 } else if (level.value === Logger.Level.OFF.value) { // OFF is Infinity
        213 return Logger.Level.OFF;
        214
        215 } else if (level.value < Logger.Level.INFO.value) {
        216 return Logger.Level.DEBUG;
        217
        218 } else if (level.value < Logger.Level.WARNING.value) {
        219 return Logger.Level.INFO;
        220
        221 } else if (level.value < Logger.Level.SEVERE.value) {
        222 return Logger.Level.WARNING;
        223
        224 } else {
        225 return Logger.Level.SEVERE;
        226 }
        227}
        228
        229
        230/**
        231 * Common log types.
        232 * @enum {string}
        233 */
        234var Type = {
        235 /** Logs originating from the browser. */
        236 BROWSER: 'browser',
        237 /** Logs from a WebDriver client. */
        238 CLIENT: 'client',
        239 /** Logs from a WebDriver implementation. */
        240 DRIVER: 'driver',
        241 /** Logs related to performance. */
        242 PERFORMANCE: 'performance',
        243 /** Logs from the remote server. */
        244 SERVER: 'server'
        245};
        246exports.Type = Type;
        247
        248
        249/**
        250 * Describes the log preferences for a WebDriver session.
        251 * @final
        252 */
        253var Preferences = goog.defineClass(null, {
        254 /** @constructor */
        255 constructor: function() {
        256 /** @private {!Object.<string, Logger.Level>} */
        257 this.prefs_ = {};
        258 },
        259
        260 /**
        261 * Sets the desired logging level for a particular log type.
        262 * @param {(string|Type)} type The log type.
        263 * @param {!Logger.Level} level The desired log level.
        264 */
        265 setLevel: function(type, level) {
        266 this.prefs_[type] = normalizeLevel(level);
        267 },
        268
        269 /**
        270 * Converts this instance to its JSON representation.
        271 * @return {!Object.<string, string>} The JSON representation of this set of
        272 * preferences.
        273 */
        274 toJSON: function() {
        275 var obj = {};
        276 for (var type in this.prefs_) {
        277 if (this.prefs_.hasOwnProperty(type)) {
        278 obj[type] = this.prefs_[type].name;
        279 }
        280 }
        281 return obj;
        282 }
        283});
        284exports.Preferences = Preferences;
        285
        286
        287/**
        288 * A single log entry recorded by a WebDriver component, such as a remote
        289 * WebDriver server.
        290 * @final
        291 */
        292var Entry = goog.defineClass(null, {
        293 /**
        294 * @param {(!Logger.Level|string)} level The entry level.
        295 * @param {string} message The log message.
        296 * @param {number=} opt_timestamp The time this entry was generated, in
        297 * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the
        298 * current time will be used.
        299 * @param {string=} opt_type The log type, if known.
        300 * @constructor
        301 */
        302 constructor: function(level, message, opt_timestamp, opt_type) {
        303
        304 /** @type {!Logger.Level} */
        305 this.level = goog.isString(level) ? getLevel(level) : level;
        306
        307 /** @type {string} */
        308 this.message = message;
        309
        310 /** @type {number} */
        311 this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now();
        312
        313 /** @type {string} */
        314 this.type = opt_type || '';
        315 },
        316
        317 statics: {
        318 /**
        319 * Converts a {@link goog.debug.LogRecord} into a
        320 * {@link webdriver.logging.Entry}.
        321 * @param {!goog.debug.LogRecord} logRecord The record to convert.
        322 * @param {string=} opt_type The log type.
        323 * @return {!Entry} The converted entry.
        324 */
        325 fromClosureLogRecord: function(logRecord, opt_type) {
        326 return new Entry(
        327 normalizeLevel(/** @type {!Logger.Level} */(logRecord.getLevel())),
        328 '[' + logRecord.getLoggerName() + '] ' + logRecord.getMessage(),
        329 logRecord.getMillis(),
        330 opt_type);
        331 }
        332 },
        333
        334 /**
        335 * @return {{level: string, message: string, timestamp: number,
        336 * type: string}} The JSON representation of this entry.
        337 */
        338 toJSON: function() {
        339 return {
        340 'level': this.level.name,
        341 'message': this.message,
        342 'timestamp': this.timestamp,
        343 'type': this.type
        344 };
        345 }
        346});
        347exports.Entry = Entry;
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/process.js.src.html b/docs/source/lib/webdriver/process.js.src.html index e0d858c..3d53566 100644 --- a/docs/source/lib/webdriver/process.js.src.html +++ b/docs/source/lib/webdriver/process.js.src.html @@ -1 +1 @@ -process.js

        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
        28goog.provide('webdriver.process');
        29
        30goog.require('goog.Uri');
        31goog.require('goog.array');
        32goog.require('goog.json');
        33
        34
        35/**
        36 * @return {boolean} Whether the current process is Node's native process
        37 * object.
        38 */
        39webdriver.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 */
        51webdriver.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 */
        63webdriver.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 */
        74webdriver.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 */
        85webdriver.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 */
        119webdriver.process.PROCESS_ = webdriver.process.IS_NATIVE_PROCESS_ ? process :
        120 webdriver.process.initBrowserProcess_();
        \ No newline at end of file +process.js

        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
        28goog.provide('webdriver.process');
        29
        30goog.require('goog.Uri');
        31goog.require('goog.array');
        32goog.require('goog.json');
        33
        34
        35/**
        36 * @return {boolean} Whether the current process is Node's native process
        37 * object.
        38 */
        39webdriver.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 */
        51webdriver.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 */
        63webdriver.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 */
        74webdriver.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 */
        85webdriver.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 */
        119webdriver.process.PROCESS_ = webdriver.process.IS_NATIVE_PROCESS_ ? process :
        120 webdriver.process.initBrowserProcess_();
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/promise.js.src.html b/docs/source/lib/webdriver/promise.js.src.html index 3867895..59098c6 100644 --- a/docs/source/lib/webdriver/promise.js.src.html +++ b/docs/source/lib/webdriver/promise.js.src.html @@ -1 +1 @@ -promise.js

        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
        49goog.provide('webdriver.promise');
        50goog.provide('webdriver.promise.ControlFlow');
        51goog.provide('webdriver.promise.ControlFlow.Timer');
        52goog.provide('webdriver.promise.Deferred');
        53goog.provide('webdriver.promise.Promise');
        54
        55goog.require('goog.array');
        56goog.require('goog.debug.Error');
        57goog.require('goog.object');
        58goog.require('webdriver.EventEmitter');
        59goog.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 * @see http://wiki.commonjs.org/wiki/Promises/A
        74 */
        75webdriver.promise.Promise = function() {
        76};
        77
        78
        79/**
        80 * Cancels the computation of this promise's value, rejecting the promise in the
        81 * process.
        82 * @param {*} reason The reason this promise is being cancelled. If not an
        83 * {@code Error}, one will be created using the value's string
        84 * representation.
        85 */
        86webdriver.promise.Promise.prototype.cancel = function(reason) {
        87 throw new TypeError('Unimplemented function: "cancel"');
        88};
        89
        90
        91/** @return {boolean} Whether this promise's value is still being computed. */
        92webdriver.promise.Promise.prototype.isPending = function() {
        93 throw new TypeError('Unimplemented function: "isPending"');
        94};
        95
        96
        97/**
        98 * Registers listeners for when this instance is resolved. This function most
        99 * overridden by subtypes.
        100 *
        101 * @param {Function=} opt_callback The function to call if this promise is
        102 * successfully resolved. The function should expect a single argument: the
        103 * promise's resolved value.
        104 * @param {Function=} opt_errback The function to call if this promise is
        105 * rejected. The function should expect a single argument: the rejection
        106 * reason.
        107 * @return {!webdriver.promise.Promise} A new promise which will be resolved
        108 * with the result of the invoked callback.
        109 */
        110webdriver.promise.Promise.prototype.then = function(
        111 opt_callback, opt_errback) {
        112 throw new TypeError('Unimplemented function: "then"');
        113};
        114
        115
        116/**
        117 * Registers a listener for when this promise is rejected. This is synonymous
        118 * with the {@code catch} clause in a synchronous API:
        119 * <pre><code>
        120 * // Synchronous API:
        121 * try {
        122 * doSynchronousWork();
        123 * } catch (ex) {
        124 * console.error(ex);
        125 * }
        126 *
        127 * // Asynchronous promise API:
        128 * doAsynchronousWork().thenCatch(function(ex) {
        129 * console.error(ex);
        130 * });
        131 * </code></pre>
        132 *
        133 * @param {!Function} errback The function to call if this promise is
        134 * rejected. The function should expect a single argument: the rejection
        135 * reason.
        136 * @return {!webdriver.promise.Promise} A new promise which will be resolved
        137 * with the result of the invoked callback.
        138 */
        139webdriver.promise.Promise.prototype.thenCatch = function(errback) {
        140 return this.then(null, errback);
        141};
        142
        143
        144/**
        145 * Registers a listener to invoke when this promise is resolved, regardless
        146 * of whether the promise's value was successfully computed. This function
        147 * is synonymous with the {@code finally} clause in a synchronous API:
        148 * <pre><code>
        149 * // Synchronous API:
        150 * try {
        151 * doSynchronousWork();
        152 * } finally {
        153 * cleanUp();
        154 * }
        155 *
        156 * // Asynchronous promise API:
        157 * doAsynchronousWork().thenFinally(cleanUp);
        158 * </code></pre>
        159 *
        160 * <b>Note:</b> similar to the {@code finally} clause, if the registered
        161 * callback returns a rejected promise or throws an error, it will silently
        162 * replace the rejection error (if any) from this promise:
        163 * <pre><code>
        164 * try {
        165 * throw Error('one');
        166 * } finally {
        167 * throw Error('two'); // Hides Error: one
        168 * }
        169 *
        170 * webdriver.promise.rejected(Error('one'))
        171 * .thenFinally(function() {
        172 * throw Error('two'); // Hides Error: one
        173 * });
        174 * </code></pre>
        175 *
        176 *
        177 * @param callback
        178 * @returns {!webdriver.promise.Promise}
        179 */
        180webdriver.promise.Promise.prototype.thenFinally = function(callback) {
        181 return this.then(callback, callback);
        182};
        183
        184
        185/**
        186 * Registers a function to be invoked when this promise is successfully
        187 * resolved. This function is provided for backwards compatibility with the
        188 * Dojo Deferred API.
        189 *
        190 * @param {Function} callback The function to call if this promise is
        191 * successfully resolved. The function should expect a single argument: the
        192 * promise's resolved value.
        193 * @param {!Object=} opt_self The object which |this| should refer to when the
        194 * function is invoked.
        195 * @return {!webdriver.promise.Promise} A new promise which will be resolved
        196 * with the result of the invoked callback.
        197 * @deprecated Use {@link #then()} instead.
        198 */
        199webdriver.promise.Promise.prototype.addCallback = function(callback, opt_self) {
        200 return this.then(goog.bind(callback, opt_self));
        201};
        202
        203
        204/**
        205 * Registers a function to be invoked when this promise is rejected.
        206 * This function is provided for backwards compatibility with the
        207 * Dojo Deferred API.
        208 *
        209 * @param {Function} errback The function to call if this promise is
        210 * rejected. The function should expect a single argument: the rejection
        211 * reason.
        212 * @param {!Object=} opt_self The object which |this| should refer to when the
        213 * function is invoked.
        214 * @return {!webdriver.promise.Promise} A new promise which will be resolved
        215 * with the result of the invoked callback.
        216 * @deprecated Use {@link #thenCatch()} instead.
        217 */
        218webdriver.promise.Promise.prototype.addErrback = function(errback, opt_self) {
        219 return this.thenCatch(goog.bind(errback, opt_self));
        220};
        221
        222
        223/**
        224 * Registers a function to be invoked when this promise is either rejected or
        225 * resolved. This function is provided for backwards compatibility with the
        226 * Dojo Deferred API.
        227 *
        228 * @param {Function} callback The function to call when this promise is
        229 * either resolved or rejected. The function should expect a single
        230 * argument: the resolved value or rejection error.
        231 * @param {!Object=} opt_self The object which |this| should refer to when the
        232 * function is invoked.
        233 * @return {!webdriver.promise.Promise} A new promise which will be resolved
        234 * with the result of the invoked callback.
        235 * @deprecated Use {@link #thenFinally()} instead.
        236 */
        237webdriver.promise.Promise.prototype.addBoth = function(callback, opt_self) {
        238 return this.thenFinally(goog.bind(callback, opt_self));
        239};
        240
        241
        242/**
        243 * An alias for {@code webdriver.promise.Promise.prototype.then} that permits
        244 * the scope of the invoked function to be specified. This function is provided
        245 * for backwards compatibility with the Dojo Deferred API.
        246 *
        247 * @param {Function} callback The function to call if this promise is
        248 * successfully resolved. The function should expect a single argument: the
        249 * promise's resolved value.
        250 * @param {Function} errback The function to call if this promise is
        251 * rejected. The function should expect a single argument: the rejection
        252 * reason.
        253 * @param {!Object=} opt_self The object which |this| should refer to when the
        254 * function is invoked.
        255 * @return {!webdriver.promise.Promise} A new promise which will be resolved
        256 * with the result of the invoked callback.
        257 * @deprecated Use {@link #then()} instead.
        258 */
        259webdriver.promise.Promise.prototype.addCallbacks = function(
        260 callback, errback, opt_self) {
        261 return this.then(goog.bind(callback, opt_self),
        262 goog.bind(errback, opt_self));
        263};
        264
        265
        266
        267/**
        268 * Represents a value that will be resolved at some point in the future. This
        269 * class represents the protected "producer" half of a Promise - each Deferred
        270 * has a {@code promise} property that may be returned to consumers for
        271 * registering callbacks, reserving the ability to resolve the deferred to the
        272 * producer.
        273 *
        274 * <p>If this Deferred is rejected and there are no listeners registered before
        275 * the next turn of the event loop, the rejection will be passed to the
        276 * {@link webdriver.promise.ControlFlow} as an unhandled failure.
        277 *
        278 * <p>If this Deferred is cancelled, the cancellation reason will be forward to
        279 * the Deferred's canceller function (if provided). The canceller may return a
        280 * truth-y value to override the reason provided for rejection.
        281 *
        282 * @param {Function=} opt_canceller Function to call when cancelling the
        283 * computation of this instance's value.
        284 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow
        285 * this instance was created under. This should only be provided during
        286 * unit tests.
        287 * @constructor
        288 * @extends {webdriver.promise.Promise}
        289 */
        290webdriver.promise.Deferred = function(opt_canceller, opt_flow) {
        291 /* NOTE: This class's implementation diverges from the prototypical style
        292 * used in the rest of the atoms library. This was done intentionally to
        293 * protect the internal Deferred state from consumers, as outlined by
        294 * http://wiki.commonjs.org/wiki/Promises
        295 */
        296 goog.base(this);
        297
        298 var flow = opt_flow || webdriver.promise.controlFlow();
        299
        300 /**
        301 * The listeners registered with this Deferred. Each element in the list will
        302 * be a 3-tuple of the callback function, errback function, and the
        303 * corresponding deferred object.
        304 * @type {!Array.<!webdriver.promise.Deferred.Listener_>}
        305 */
        306 var listeners = [];
        307
        308 /**
        309 * Whether this Deferred's resolution was ever handled by a listener.
        310 * If the Deferred is rejected and its value is not handled by a listener
        311 * before the next turn of the event loop, the error will be passed to the
        312 * global error handler.
        313 * @type {boolean}
        314 */
        315 var handled = false;
        316
        317 /**
        318 * Key for the timeout used to delay reproting an unhandled rejection to the
        319 * parent {@link webdriver.promise.ControlFlow}.
        320 * @type {?number}
        321 */
        322 var pendingRejectionKey = null;
        323
        324 /**
        325 * This Deferred's current state.
        326 * @type {!webdriver.promise.Deferred.State_}
        327 */
        328 var state = webdriver.promise.Deferred.State_.PENDING;
        329
        330 /**
        331 * This Deferred's resolved value; set when the state transitions from
        332 * {@code webdriver.promise.Deferred.State_.PENDING}.
        333 * @type {*}
        334 */
        335 var value;
        336
        337 /** @return {boolean} Whether this promise's value is still pending. */
        338 function isPending() {
        339 return state == webdriver.promise.Deferred.State_.PENDING;
        340 }
        341
        342 /**
        343 * Removes all of the listeners previously registered on this deferred.
        344 * @throws {Error} If this deferred has already been resolved.
        345 */
        346 function removeAll() {
        347 listeners = [];
        348 }
        349
        350 /**
        351 * Resolves this deferred. If the new value is a promise, this function will
        352 * wait for it to be resolved before notifying the registered listeners.
        353 * @param {!webdriver.promise.Deferred.State_} newState The deferred's new
        354 * state.
        355 * @param {*} newValue The deferred's new value.
        356 */
        357 function resolve(newState, newValue) {
        358 if (webdriver.promise.Deferred.State_.PENDING !== state) {
        359 return;
        360 }
        361
        362 state = webdriver.promise.Deferred.State_.BLOCKED;
        363
        364 if (webdriver.promise.isPromise(newValue) && newValue !== self) {
        365 var onFulfill = goog.partial(notifyAll, newState);
        366 var onReject = goog.partial(
        367 notifyAll, webdriver.promise.Deferred.State_.REJECTED);
        368 if (newValue instanceof webdriver.promise.Deferred) {
        369 newValue.then(onFulfill, onReject);
        370 } else {
        371 webdriver.promise.asap(newValue, onFulfill, onReject);
        372 }
        373
        374 } else {
        375 notifyAll(newState, newValue);
        376 }
        377 }
        378
        379 /**
        380 * Notifies all of the listeners registered with this Deferred that its state
        381 * has changed.
        382 * @param {!webdriver.promise.Deferred.State_} newState The deferred's new
        383 * state.
        384 * @param {*} newValue The deferred's new value.
        385 */
        386 function notifyAll(newState, newValue) {
        387 if (newState === webdriver.promise.Deferred.State_.REJECTED &&
        388 // We cannot check instanceof Error since the object may have been
        389 // created in a different JS context.
        390 goog.isObject(newValue) && goog.isString(newValue.message)) {
        391 newValue = flow.annotateError(/** @type {!Error} */(newValue));
        392 }
        393
        394 state = newState;
        395 value = newValue;
        396 while (listeners.length) {
        397 notify(listeners.shift());
        398 }
        399
        400 if (!handled && state == webdriver.promise.Deferred.State_.REJECTED) {
        401 pendingRejectionKey = propagateError(value);
        402 }
        403 }
        404
        405 /**
        406 * Propagates an unhandled rejection to the parent ControlFlow in a
        407 * future turn of the JavaScript event loop.
        408 * @param {*} error The error value to report.
        409 * @return {number} The key for the registered timeout.
        410 */
        411 function propagateError(error) {
        412 flow.pendingRejections_ += 1;
        413 return flow.timer.setTimeout(function() {
        414 flow.pendingRejections_ -= 1;
        415 flow.abortFrame_(error);
        416 }, 0);
        417 }
        418
        419 /**
        420 * Notifies a single listener of this Deferred's change in state.
        421 * @param {!webdriver.promise.Deferred.Listener_} listener The listener to
        422 * notify.
        423 */
        424 function notify(listener) {
        425 var func = state == webdriver.promise.Deferred.State_.RESOLVED ?
        426 listener.callback : listener.errback;
        427 if (func) {
        428 flow.runInNewFrame_(goog.partial(func, value),
        429 listener.fulfill, listener.reject);
        430 } else if (state == webdriver.promise.Deferred.State_.REJECTED) {
        431 listener.reject(value);
        432 } else {
        433 listener.fulfill(value);
        434 }
        435 }
        436
        437 /**
        438 * The consumer promise for this instance. Provides protected access to the
        439 * callback registering functions.
        440 * @type {!webdriver.promise.Promise}
        441 */
        442 var promise = new webdriver.promise.Promise();
        443
        444 /**
        445 * Registers a callback on this Deferred.
        446 * @param {Function=} opt_callback The callback.
        447 * @param {Function=} opt_errback The errback.
        448 * @return {!webdriver.promise.Promise} A new promise representing the result
        449 * of the callback.
        450 * @see webdriver.promise.Promise#then
        451 */
        452 function then(opt_callback, opt_errback) {
        453 // Avoid unnecessary allocations if we weren't given any callback functions.
        454 if (!opt_callback && !opt_errback) {
        455 return promise;
        456 }
        457
        458 // The moment a listener is registered, we consider this deferred to be
        459 // handled; the callback must handle any rejection errors.
        460 handled = true;
        461 if (pendingRejectionKey) {
        462 flow.pendingRejections_ -= 1;
        463 flow.timer.clearTimeout(pendingRejectionKey);
        464 }
        465
        466 var deferred = new webdriver.promise.Deferred(cancel, flow);
        467 var listener = {
        468 callback: opt_callback,
        469 errback: opt_errback,
        470 fulfill: deferred.fulfill,
        471 reject: deferred.reject
        472 };
        473
        474 if (state == webdriver.promise.Deferred.State_.PENDING ||
        475 state == webdriver.promise.Deferred.State_.BLOCKED) {
        476 listeners.push(listener);
        477 } else {
        478 notify(listener);
        479 }
        480
        481 return deferred.promise;
        482 }
        483
        484 var self = this;
        485
        486 /**
        487 * Resolves this promise with the given value. If the value is itself a
        488 * promise and not a reference to this deferred, this instance will wait for
        489 * it before resolving.
        490 * @param {*=} opt_value The resolved value.
        491 */
        492 function fulfill(opt_value) {
        493 resolve(webdriver.promise.Deferred.State_.RESOLVED, opt_value);
        494 }
        495
        496 /**
        497 * Rejects this promise. If the error is itself a promise, this instance will
        498 * be chained to it and be rejected with the error's resolved value.
        499 * @param {*=} opt_error The rejection reason, typically either a
        500 * {@code Error} or a {@code string}.
        501 */
        502 function reject(opt_error) {
        503 resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error);
        504 }
        505
        506 /**
        507 * Attempts to cancel the computation of this instance's value. This attempt
        508 * will silently fail if this instance has already resolved.
        509 * @param {*=} opt_reason The reason for cancelling this promise.
        510 */
        511 function cancel(opt_reason) {
        512 if (!isPending()) {
        513 return;
        514 }
        515
        516 if (opt_canceller) {
        517 opt_reason = opt_canceller(opt_reason) || opt_reason;
        518 }
        519
        520 reject(opt_reason);
        521 }
        522
        523 this.promise = promise;
        524 this.promise.then = this.then = then;
        525 this.promise.cancel = this.cancel = cancel;
        526 this.promise.isPending = this.isPending = isPending;
        527 this.fulfill = fulfill;
        528 this.reject = this.errback = reject;
        529
        530 // Only expose this function to our internal classes.
        531 // TODO: find a cleaner way of handling this.
        532 if (this instanceof webdriver.promise.Task_) {
        533 this.removeAll = removeAll;
        534 }
        535
        536 // Export symbols necessary for the contract on this object to work in
        537 // compiled mode.
        538 goog.exportProperty(this, 'then', this.then);
        539 goog.exportProperty(this, 'cancel', cancel);
        540 goog.exportProperty(this, 'fulfill', fulfill);
        541 goog.exportProperty(this, 'reject', reject);
        542 goog.exportProperty(this, 'isPending', isPending);
        543 goog.exportProperty(this, 'promise', this.promise);
        544 goog.exportProperty(this.promise, 'then', this.then);
        545 goog.exportProperty(this.promise, 'cancel', cancel);
        546 goog.exportProperty(this.promise, 'isPending', isPending);
        547};
        548goog.inherits(webdriver.promise.Deferred, webdriver.promise.Promise);
        549
        550
        551/**
        552 * Type definition for a listener registered on a Deferred object.
        553 * @typedef {{callback:(Function|undefined),
        554 * errback:(Function|undefined),
        555 * fulfill: function(*), reject: function(*)}}
        556 * @private
        557 */
        558webdriver.promise.Deferred.Listener_;
        559
        560
        561/**
        562 * The three states a {@link webdriver.promise.Deferred} object may be in.
        563 * @enum {number}
        564 * @private
        565 */
        566webdriver.promise.Deferred.State_ = {
        567 REJECTED: -1,
        568 PENDING: 0,
        569 BLOCKED: 1,
        570 RESOLVED: 2
        571};
        572
        573
        574/**
        575 * Tests if a value is an Error-like object. This is more than an straight
        576 * instanceof check since the value may originate from another context.
        577 * @param {*} value The value to test.
        578 * @return {boolean} Whether the value is an error.
        579 * @private
        580 */
        581webdriver.promise.isError_ = function(value) {
        582 return value instanceof Error ||
        583 goog.isObject(value) &&
        584 (Object.prototype.toString.call(value) === '[object Error]' ||
        585 // A special test for goog.testing.JsUnitException.
        586 value.isJsUnitException);
        587
        588};
        589
        590
        591/**
        592 * Determines whether a {@code value} should be treated as a promise.
        593 * Any object whose "then" property is a function will be considered a promise.
        594 *
        595 * @param {*} value The value to test.
        596 * @return {boolean} Whether the value is a promise.
        597 */
        598webdriver.promise.isPromise = function(value) {
        599 return !!value && goog.isObject(value) &&
        600 // Use array notation so the Closure compiler does not obfuscate away our
        601 // contract.
        602 goog.isFunction(value['then']);
        603};
        604
        605
        606/**
        607 * Creates a promise that will be resolved at a set time in the future.
        608 * @param {number} ms The amount of time, in milliseconds, to wait before
        609 * resolving the promise.
        610 * @return {!webdriver.promise.Promise} The promise.
        611 */
        612webdriver.promise.delayed = function(ms) {
        613 var timer = webdriver.promise.controlFlow().timer;
        614 var key;
        615 var deferred = new webdriver.promise.Deferred(function() {
        616 timer.clearTimeout(key);
        617 });
        618 key = timer.setTimeout(deferred.fulfill, ms);
        619 return deferred.promise;
        620};
        621
        622
        623/**
        624 * Creates a new deferred object.
        625 * @param {Function=} opt_canceller Function to call when cancelling the
        626 * computation of this instance's value.
        627 * @return {!webdriver.promise.Deferred} The new deferred object.
        628 */
        629webdriver.promise.defer = function(opt_canceller) {
        630 return new webdriver.promise.Deferred(opt_canceller);
        631};
        632
        633
        634/**
        635 * Creates a promise that has been resolved with the given value.
        636 * @param {*=} opt_value The resolved value.
        637 * @return {!webdriver.promise.Promise} The resolved promise.
        638 */
        639webdriver.promise.fulfilled = function(opt_value) {
        640 if (opt_value instanceof webdriver.promise.Promise) {
        641 return opt_value;
        642 }
        643 var deferred = new webdriver.promise.Deferred();
        644 deferred.fulfill(opt_value);
        645 return deferred.promise;
        646};
        647
        648
        649/**
        650 * Creates a promise that has been rejected with the given reason.
        651 * @param {*=} opt_reason The rejection reason; may be any value, but is
        652 * usually an Error or a string.
        653 * @return {!webdriver.promise.Promise} The rejected promise.
        654 */
        655webdriver.promise.rejected = function(opt_reason) {
        656 var deferred = new webdriver.promise.Deferred();
        657 deferred.reject(opt_reason);
        658 return deferred.promise;
        659};
        660
        661
        662/**
        663 * Wraps a function that is assumed to be a node-style callback as its final
        664 * argument. This callback takes two arguments: an error value (which will be
        665 * null if the call succeeded), and the success value as the second argument.
        666 * If the call fails, the returned promise will be rejected, otherwise it will
        667 * be resolved with the result.
        668 * @param {!Function} fn The function to wrap.
        669 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        670 * result of the provided function's callback.
        671 */
        672webdriver.promise.checkedNodeCall = function(fn) {
        673 var deferred = new webdriver.promise.Deferred(function() {
        674 throw Error('This Deferred may not be cancelled');
        675 });
        676 try {
        677 fn(function(error, value) {
        678 error ? deferred.reject(error) : deferred.fulfill(value);
        679 });
        680 } catch (ex) {
        681 deferred.reject(ex);
        682 }
        683 return deferred.promise;
        684};
        685
        686
        687/**
        688 * Registers an observer on a promised {@code value}, returning a new promise
        689 * that will be resolved when the value is. If {@code value} is not a promise,
        690 * then the return promise will be immediately resolved.
        691 * @param {*} value The value to observe.
        692 * @param {Function=} opt_callback The function to call when the value is
        693 * resolved successfully.
        694 * @param {Function=} opt_errback The function to call when the value is
        695 * rejected.
        696 * @return {!webdriver.promise.Promise} A new promise.
        697 */
        698webdriver.promise.when = function(value, opt_callback, opt_errback) {
        699 if (value instanceof webdriver.promise.Promise) {
        700 return value.then(opt_callback, opt_errback);
        701 }
        702
        703 var deferred = new webdriver.promise.Deferred();
        704
        705 webdriver.promise.asap(value, deferred.fulfill, deferred.reject);
        706
        707 return deferred.then(opt_callback, opt_errback);
        708};
        709
        710
        711/**
        712 * Invokes the appropriate callback function as soon as a promised
        713 * {@code value} is resolved. This function is similar to
        714 * {@link webdriver.promise.when}, except it does not return a new promise.
        715 * @param {*} value The value to observe.
        716 * @param {Function} callback The function to call when the value is
        717 * resolved successfully.
        718 * @param {Function=} opt_errback The function to call when the value is
        719 * rejected.
        720 */
        721webdriver.promise.asap = function(value, callback, opt_errback) {
        722 if (webdriver.promise.isPromise(value)) {
        723 value.then(callback, opt_errback);
        724
        725 // Maybe a Dojo-like deferred object?
        726 } else if (!!value && goog.isObject(value) &&
        727 goog.isFunction(value.addCallbacks)) {
        728 value.addCallbacks(callback, opt_errback);
        729
        730 // A raw value, return a resolved promise.
        731 } else if (callback) {
        732 callback(value);
        733 }
        734};
        735
        736
        737/**
        738 * Given an array of promises, will return a promise that will be fulfilled
        739 * with the fulfillment values of the input array's values. If any of the
        740 * input array's promises are rejected, the returned promise will be rejected
        741 * with the same reason.
        742 *
        743 * @param {!Array.<(T|!webdriver.promise.Promise.<T>)>} arr An array of
        744 * promises to wait on.
        745 * @return {!webdriver.promise.Promise.<!Array.<T>>} A promise that is
        746 * fulfilled with an array containing the fulfilled values of the
        747 * input array, or rejected with the same reason as the first
        748 * rejected value.
        749 * @template T
        750 */
        751webdriver.promise.all = function(arr) {
        752 var n = arr.length;
        753 if (!n) {
        754 return webdriver.promise.fulfilled([]);
        755 }
        756
        757 var toFulfill = n;
        758 var result = webdriver.promise.defer();
        759 var values = [];
        760
        761 var onFulfill = function(index, value) {
        762 values[index] = value;
        763 toFulfill--;
        764 if (toFulfill == 0) {
        765 result.fulfill(values);
        766 }
        767 };
        768
        769 for (var i = 0; i < n; ++i) {
        770 webdriver.promise.asap(
        771 arr[i], goog.partial(onFulfill, i), result.reject);
        772 }
        773
        774 return result.promise;
        775};
        776
        777
        778/**
        779 * Calls a function for each element in an array and inserts the result into a
        780 * new array, which is used as the fulfillment value of the promise returned
        781 * by this function.
        782 *
        783 * <p>If the return value of the mapping function is a promise, this function
        784 * will wait for it to be fulfilled before inserting it into the new array.
        785 *
        786 * <p>If the mapping function throws or returns a rejected promise, the
        787 * promise returned by this function will be rejected with the same reason.
        788 * Only the first failure will be reported; all subsequent errors will be
        789 * silently ignored.
        790 *
        791 * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The
        792 * array to iterator over, or a promise that will resolve to said array.
        793 * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): ?} fn The
        794 * function to call for each element in the array. This function should
        795 * expect three arguments (the element, the index, and the array itself.
        796 * @param {SELF=} opt_self The object to be used as the value of 'this' within
        797 * {@code fn}.
        798 * @template TYPE, SELF
        799 */
        800webdriver.promise.map = function(arr, fn, opt_self) {
        801 return webdriver.promise.when(arr, function(arr) {
        802 var result = goog.array.map(arr, fn, opt_self);
        803 return webdriver.promise.all(result);
        804 });
        805};
        806
        807
        808/**
        809 * Calls a function for each element in an array, and if the function returns
        810 * true adds the element to a new array.
        811 *
        812 * <p>If the return value of the filter function is a promise, this function
        813 * will wait for it to be fulfilled before determining whether to insert the
        814 * element into the new array.
        815 *
        816 * <p>If the filter function throws or returns a rejected promise, the promise
        817 * returned by this function will be rejected with the same reason. Only the
        818 * first failure will be reported; all subsequent errors will be silently
        819 * ignored.
        820 *
        821 * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The
        822 * array to iterator over, or a promise that will resolve to said array.
        823 * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): (
        824 * boolean|webdriver.promise.Promise.<boolean>)} fn The function
        825 * to call for each element in the array.
        826 * @param {SELF=} opt_self The object to be used as the value of 'this' within
        827 * {@code fn}.
        828 * @template TYPE, SELF
        829 */
        830webdriver.promise.filter = function(arr, fn, opt_self) {
        831 return webdriver.promise.when(arr, function(arr) {
        832 var originalValues = goog.array.clone(arr);
        833 return webdriver.promise.map(arr, fn, opt_self).then(function(include) {
        834 return goog.array.filter(originalValues, function(value, index) {
        835 return include[index];
        836 });
        837 });
        838 });
        839};
        840
        841
        842/**
        843 * Returns a promise that will be resolved with the input value in a
        844 * fully-resolved state. If the value is an array, each element will be fully
        845 * resolved. Likewise, if the value is an object, all keys will be fully
        846 * resolved. In both cases, all nested arrays and objects will also be
        847 * fully resolved. All fields are resolved in place; the returned promise will
        848 * resolve on {@code value} and not a copy.
        849 *
        850 * Warning: This function makes no checks against objects that contain
        851 * cyclical references:
        852 * <pre><code>
        853 * var value = {};
        854 * value['self'] = value;
        855 * webdriver.promise.fullyResolved(value); // Stack overflow.
        856 * </code></pre>
        857 *
        858 * @param {*} value The value to fully resolve.
        859 * @return {!webdriver.promise.Promise} A promise for a fully resolved version
        860 * of the input value.
        861 */
        862webdriver.promise.fullyResolved = function(value) {
        863 if (webdriver.promise.isPromise(value)) {
        864 return webdriver.promise.when(value, webdriver.promise.fullyResolveValue_);
        865 }
        866 return webdriver.promise.fullyResolveValue_(value);
        867};
        868
        869
        870/**
        871 * @param {*} value The value to fully resolve. If a promise, assumed to
        872 * already be resolved.
        873 * @return {!webdriver.promise.Promise} A promise for a fully resolved version
        874 * of the input value.
        875 * @private
        876 */
        877webdriver.promise.fullyResolveValue_ = function(value) {
        878 switch (goog.typeOf(value)) {
        879 case 'array':
        880 return webdriver.promise.fullyResolveKeys_(
        881 /** @type {!Array} */ (value));
        882
        883 case 'object':
        884 if (webdriver.promise.isPromise(value)) {
        885 // We get here when the original input value is a promise that
        886 // resolves to itself. When the user provides us with such a promise,
        887 // trust that it counts as a "fully resolved" value and return it.
        888 // Of course, since it's already a promise, we can just return it
        889 // to the user instead of wrapping it in another promise.
        890 return /** @type {!webdriver.promise.Promise} */ (value);
        891 }
        892
        893 if (goog.isNumber(value.nodeType) &&
        894 goog.isObject(value.ownerDocument) &&
        895 goog.isNumber(value.ownerDocument.nodeType)) {
        896 // DOM node; return early to avoid infinite recursion. Should we
        897 // only support objects with a certain level of nesting?
        898 return webdriver.promise.fulfilled(value);
        899 }
        900
        901 return webdriver.promise.fullyResolveKeys_(
        902 /** @type {!Object} */ (value));
        903
        904 default: // boolean, function, null, number, string, undefined
        905 return webdriver.promise.fulfilled(value);
        906 }
        907};
        908
        909
        910/**
        911 * @param {!(Array|Object)} obj the object to resolve.
        912 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        913 * input object once all of its values have been fully resolved.
        914 * @private
        915 */
        916webdriver.promise.fullyResolveKeys_ = function(obj) {
        917 var isArray = goog.isArray(obj);
        918 var numKeys = isArray ? obj.length : goog.object.getCount(obj);
        919 if (!numKeys) {
        920 return webdriver.promise.fulfilled(obj);
        921 }
        922
        923 var numResolved = 0;
        924 var deferred = new webdriver.promise.Deferred();
        925
        926 // In pre-IE9, goog.array.forEach will not iterate properly over arrays
        927 // containing undefined values because "index in array" returns false
        928 // when array[index] === undefined (even for x = [undefined, 1]). To get
        929 // around this, we need to use our own forEach implementation.
        930 // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be
        931 // reproduced in IE9 by changing the browser/document modes, it requires an
        932 // actual pre-IE9 browser. Yay, IE!
        933 var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) {
        934 var n = arr.length;
        935 for (var i = 0; i < n; ++i) {
        936 fn.call(null, arr[i], i, arr);
        937 }
        938 };
        939
        940 forEachKey(obj, function(partialValue, key) {
        941 var type = goog.typeOf(partialValue);
        942 if (type != 'array' && type != 'object') {
        943 maybeResolveValue();
        944 return;
        945 }
        946
        947 webdriver.promise.fullyResolved(partialValue).then(
        948 function(resolvedValue) {
        949 obj[key] = resolvedValue;
        950 maybeResolveValue();
        951 },
        952 deferred.reject);
        953 });
        954
        955 return deferred.promise;
        956
        957 function maybeResolveValue() {
        958 if (++numResolved == numKeys) {
        959 deferred.fulfill(obj);
        960 }
        961 }
        962};
        963
        964
        965//////////////////////////////////////////////////////////////////////////////
        966//
        967// webdriver.promise.ControlFlow
        968//
        969//////////////////////////////////////////////////////////////////////////////
        970
        971
        972
        973/**
        974 * Handles the execution of scheduled tasks, each of which may be an
        975 * asynchronous operation. The control flow will ensure tasks are executed in
        976 * the ordered scheduled, starting each task only once those before it have
        977 * completed.
        978 *
        979 * <p>Each task scheduled within this flow may return a
        980 * {@link webdriver.promise.Promise} to indicate it is an asynchronous
        981 * operation. The ControlFlow will wait for such promises to be resolved before
        982 * marking the task as completed.
        983 *
        984 * <p>Tasks and each callback registered on a {@link webdriver.promise.Deferred}
        985 * will be run in their own ControlFlow frame. Any tasks scheduled within a
        986 * frame will have priority over previously scheduled tasks. Furthermore, if
        987 * any of the tasks in the frame fails, the remainder of the tasks in that frame
        988 * will be discarded and the failure will be propagated to the user through the
        989 * callback/task's promised result.
        990 *
        991 * <p>Each time a ControlFlow empties its task queue, it will fire an
        992 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Conversely,
        993 * whenever the flow terminates due to an unhandled error, it will remove all
        994 * remaining tasks in its queue and fire an
        995 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event. If
        996 * there are no listeners registered with the flow, the error will be
        997 * rethrown to the global error handler.
        998 *
        999 * @param {webdriver.promise.ControlFlow.Timer=} opt_timer The timer object
        1000 * to use. Should only be set for testing.
        1001 * @constructor
        1002 * @extends {webdriver.EventEmitter}
        1003 */
        1004webdriver.promise.ControlFlow = function(opt_timer) {
        1005 webdriver.EventEmitter.call(this);
        1006
        1007 /**
        1008 * The timer used by this instance.
        1009 * @type {webdriver.promise.ControlFlow.Timer}
        1010 */
        1011 this.timer = opt_timer || webdriver.promise.ControlFlow.defaultTimer;
        1012
        1013 /**
        1014 * A list of recent tasks. Each time a new task is started, or a frame is
        1015 * completed, the previously recorded task is removed from this list. If
        1016 * there are multiple tasks, task N+1 is considered a sub-task of task
        1017 * N.
        1018 * @private {!Array.<!webdriver.promise.Task_>}
        1019 */
        1020 this.history_ = [];
        1021};
        1022goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter);
        1023
        1024
        1025/**
        1026 * @typedef {{clearInterval: function(number),
        1027 * clearTimeout: function(number),
        1028 * setInterval: function(!Function, number): number,
        1029 * setTimeout: function(!Function, number): number}}
        1030 */
        1031webdriver.promise.ControlFlow.Timer;
        1032
        1033
        1034/**
        1035 * The default timer object, which uses the global timer functions.
        1036 * @type {webdriver.promise.ControlFlow.Timer}
        1037 */
        1038webdriver.promise.ControlFlow.defaultTimer = (function() {
        1039 // The default timer functions may be defined as free variables for the
        1040 // current context, so do not reference them using "window" or
        1041 // "goog.global". Also, we must invoke them in a closure, and not using
        1042 // bind(), so we do not get "TypeError: Illegal invocation" (WebKit) or
        1043 // "Invalid calling object" (IE) errors.
        1044 return {
        1045 clearInterval: wrap(clearInterval),
        1046 clearTimeout: wrap(clearTimeout),
        1047 setInterval: wrap(setInterval),
        1048 setTimeout: wrap(setTimeout)
        1049 };
        1050
        1051 function wrap(fn) {
        1052 return function() {
        1053 // Cannot use .call() or .apply() since we do not know which variable
        1054 // the function is bound to, and using the wrong one will generate
        1055 // an error.
        1056 return fn(arguments[0], arguments[1]);
        1057 };
        1058 }
        1059})();
        1060
        1061
        1062/**
        1063 * Events that may be emitted by an {@link webdriver.promise.ControlFlow}.
        1064 * @enum {string}
        1065 */
        1066webdriver.promise.ControlFlow.EventType = {
        1067
        1068 /** Emitted when all tasks have been successfully executed. */
        1069 IDLE: 'idle',
        1070
        1071 /** Emitted whenever a new task has been scheduled. */
        1072 SCHEDULE_TASK: 'scheduleTask',
        1073
        1074 /**
        1075 * Emitted whenever a control flow aborts due to an unhandled promise
        1076 * rejection. This event will be emitted along with the offending rejection
        1077 * reason. Upon emitting this event, the control flow will empty its task
        1078 * queue and revert to its initial state.
        1079 */
        1080 UNCAUGHT_EXCEPTION: 'uncaughtException'
        1081};
        1082
        1083
        1084/**
        1085 * How often, in milliseconds, the event loop should run.
        1086 * @type {number}
        1087 * @const
        1088 */
        1089webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY = 10;
        1090
        1091
        1092/**
        1093 * Tracks the active execution frame for this instance. Lazily initialized
        1094 * when the first task is scheduled.
        1095 * @private {webdriver.promise.Frame_}
        1096 */
        1097webdriver.promise.ControlFlow.prototype.activeFrame_ = null;
        1098
        1099
        1100/**
        1101 * A reference to the frame in which new tasks should be scheduled. If
        1102 * {@code null}, tasks will be scheduled within the active frame. When forcing
        1103 * a function to run in the context of a new frame, this pointer is used to
        1104 * ensure tasks are scheduled within the newly created frame, even though it
        1105 * won't be active yet.
        1106 * @private {webdriver.promise.Frame_}
        1107 * @see {#runInNewFrame_}
        1108 */
        1109webdriver.promise.ControlFlow.prototype.schedulingFrame_ = null;
        1110
        1111
        1112/**
        1113 * Timeout ID set when the flow is about to shutdown without any errors
        1114 * being detected. Upon shutting down, the flow will emit an
        1115 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events
        1116 * always follow a brief timeout in order to catch latent errors from the last
        1117 * completed task. If this task had a callback registered, but no errback, and
        1118 * the task fails, the unhandled failure would not be reported by the promise
        1119 * system until the next turn of the event loop:
        1120 *
        1121 * // Schedule 1 task that fails.
        1122 * var result = webriver.promise.controlFlow().schedule('example',
        1123 * function() { return webdriver.promise.rejected('failed'); });
        1124 * // Set a callback on the result. This delays reporting the unhandled
        1125 * // failure for 1 turn of the event loop.
        1126 * result.then(goog.nullFunction);
        1127 *
        1128 * @private {?number}
        1129 */
        1130webdriver.promise.ControlFlow.prototype.shutdownId_ = null;
        1131
        1132
        1133/**
        1134 * Interval ID for this instance's event loop.
        1135 * @private {?number}
        1136 */
        1137webdriver.promise.ControlFlow.prototype.eventLoopId_ = null;
        1138
        1139
        1140/**
        1141 * The number of "pending" promise rejections.
        1142 *
        1143 * <p>Each time a promise is rejected and is not handled by a listener, it will
        1144 * schedule a 0-based timeout to check if it is still unrejected in the next
        1145 * turn of the JS-event loop. This allows listeners to attach to, and handle,
        1146 * the rejected promise at any point in same turn of the event loop that the
        1147 * promise was rejected.
        1148 *
        1149 * <p>When this flow's own event loop triggers, it will not run if there
        1150 * are any outstanding promise rejections. This allows unhandled promises to
        1151 * be reported before a new task is started, ensuring the error is reported to
        1152 * the current task queue.
        1153 *
        1154 * @private {number}
        1155 */
        1156webdriver.promise.ControlFlow.prototype.pendingRejections_ = 0;
        1157
        1158
        1159/**
        1160 * The number of aborted frames since the last time a task was executed or a
        1161 * frame completed successfully.
        1162 * @private {number}
        1163 */
        1164webdriver.promise.ControlFlow.prototype.numAbortedFrames_ = 0;
        1165
        1166
        1167/**
        1168 * Resets this instance, clearing its queue and removing all event listeners.
        1169 */
        1170webdriver.promise.ControlFlow.prototype.reset = function() {
        1171 this.activeFrame_ = null;
        1172 this.clearHistory();
        1173 this.removeAllListeners();
        1174 this.cancelShutdown_();
        1175 this.cancelEventLoop_();
        1176};
        1177
        1178
        1179/**
        1180 * Returns a summary of the recent task activity for this instance. This
        1181 * includes the most recently completed task, as well as any parent tasks. In
        1182 * the returned summary, the task at index N is considered a sub-task of the
        1183 * task at index N+1.
        1184 * @return {!Array.<string>} A summary of this instance's recent task
        1185 * activity.
        1186 */
        1187webdriver.promise.ControlFlow.prototype.getHistory = function() {
        1188 var pendingTasks = [];
        1189 var currentFrame = this.activeFrame_;
        1190 while (currentFrame) {
        1191 var task = currentFrame.getPendingTask();
        1192 if (task) {
        1193 pendingTasks.push(task);
        1194 }
        1195 // A frame's parent node will always be another frame.
        1196 currentFrame =
        1197 /** @type {webdriver.promise.Frame_} */ (currentFrame.getParent());
        1198 }
        1199
        1200 var fullHistory = goog.array.concat(this.history_, pendingTasks);
        1201 return goog.array.map(fullHistory, function(task) {
        1202 return task.toString();
        1203 });
        1204};
        1205
        1206
        1207/** Clears this instance's task history. */
        1208webdriver.promise.ControlFlow.prototype.clearHistory = function() {
        1209 this.history_ = [];
        1210};
        1211
        1212
        1213/**
        1214 * Removes a completed task from this instance's history record. If any
        1215 * tasks remain from aborted frames, those will be removed as well.
        1216 * @private
        1217 */
        1218webdriver.promise.ControlFlow.prototype.trimHistory_ = function() {
        1219 if (this.numAbortedFrames_) {
        1220 goog.array.splice(this.history_,
        1221 this.history_.length - this.numAbortedFrames_,
        1222 this.numAbortedFrames_);
        1223 this.numAbortedFrames_ = 0;
        1224 }
        1225 this.history_.pop();
        1226};
        1227
        1228
        1229/**
        1230 * Property used to track whether an error has been annotated by
        1231 * {@link webdriver.promise.ControlFlow#annotateError}.
        1232 * @private {string}
        1233 * @const
        1234 */
        1235webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_ =
        1236 'webdriver_promise_error_';
        1237
        1238
        1239/**
        1240 * Appends a summary of this instance's recent task history to the given
        1241 * error's stack trace. This function will also ensure the error's stack trace
        1242 * is in canonical form.
        1243 * @param {!(Error|goog.testing.JsUnitException)} e The error to annotate.
        1244 * @return {!(Error|goog.testing.JsUnitException)} The annotated error.
        1245 */
        1246webdriver.promise.ControlFlow.prototype.annotateError = function(e) {
        1247 if (!!e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_]) {
        1248 return e;
        1249 }
        1250
        1251 var history = this.getHistory();
        1252 if (history.length) {
        1253 e = webdriver.stacktrace.format(e);
        1254
        1255 /** @type {!Error} */(e).stack += [
        1256 '\n==== async task ====\n',
        1257 history.join('\n==== async task ====\n')
        1258 ].join('');
        1259
        1260 e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_] = true;
        1261 }
        1262
        1263 return e;
        1264};
        1265
        1266
        1267/**
        1268 * @return {string} The scheduled tasks still pending with this instance.
        1269 */
        1270webdriver.promise.ControlFlow.prototype.getSchedule = function() {
        1271 return this.activeFrame_ ? this.activeFrame_.getRoot().toString() : '[]';
        1272};
        1273
        1274
        1275/**
        1276 * Schedules a task for execution. If there is nothing currently in the
        1277 * queue, the task will be executed in the next turn of the event loop.
        1278 *
        1279 * @param {!Function} fn The function to call to start the task. If the
        1280 * function returns a {@link webdriver.promise.Promise}, this instance
        1281 * will wait for it to be resolved before starting the next task.
        1282 * @param {string=} opt_description A description of the task.
        1283 * @return {!webdriver.promise.Promise} A promise that will be resolved with
        1284 * the result of the action.
        1285 */
        1286webdriver.promise.ControlFlow.prototype.execute = function(
        1287 fn, opt_description) {
        1288 this.cancelShutdown_();
        1289
        1290 if (!this.activeFrame_) {
        1291 this.activeFrame_ = new webdriver.promise.Frame_(this);
        1292 }
        1293
        1294 // Trim an extra frame off the generated stack trace for the call to this
        1295 // function.
        1296 var snapshot = new webdriver.stacktrace.Snapshot(1);
        1297 var task = new webdriver.promise.Task_(
        1298 this, fn, opt_description || '', snapshot);
        1299 var scheduleIn = this.schedulingFrame_ || this.activeFrame_;
        1300 scheduleIn.addChild(task);
        1301
        1302 this.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK);
        1303
        1304 this.scheduleEventLoopStart_();
        1305 return task.promise;
        1306};
        1307
        1308
        1309/**
        1310 * Inserts a {@code setTimeout} into the command queue. This is equivalent to
        1311 * a thread sleep in a synchronous programming language.
        1312 *
        1313 * @param {number} ms The timeout delay, in milliseconds.
        1314 * @param {string=} opt_description A description to accompany the timeout.
        1315 * @return {!webdriver.promise.Promise} A promise that will be resolved with
        1316 * the result of the action.
        1317 */
        1318webdriver.promise.ControlFlow.prototype.timeout = function(
        1319 ms, opt_description) {
        1320 return this.execute(function() {
        1321 return webdriver.promise.delayed(ms);
        1322 }, opt_description);
        1323};
        1324
        1325
        1326/**
        1327 * Schedules a task that shall wait for a condition to hold. Each condition
        1328 * function may return any value, but it will always be evaluated as a boolean.
        1329 *
        1330 * <p>Condition functions may schedule sub-tasks with this instance, however,
        1331 * their execution time will be factored into whether a wait has timed out.
        1332 *
        1333 * <p>In the event a condition returns a Promise, the polling loop will wait for
        1334 * it to be resolved before evaluating whether the condition has been satisfied.
        1335 * The resolution time for a promise is factored into whether a wait has timed
        1336 * out.
        1337 *
        1338 * <p>If the condition function throws, or returns a rejected promise, the
        1339 * wait task will fail.
        1340 *
        1341 * @param {!Function} condition The condition function to poll.
        1342 * @param {number} timeout How long to wait, in milliseconds, for the condition
        1343 * to hold before timing out.
        1344 * @param {string=} opt_message An optional error message to include if the
        1345 * wait times out; defaults to the empty string.
        1346 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1347 * condition has been satisified. The promise shall be rejected if the wait
        1348 * times out waiting for the condition.
        1349 */
        1350webdriver.promise.ControlFlow.prototype.wait = function(
        1351 condition, timeout, opt_message) {
        1352 var sleep = Math.min(timeout, 100);
        1353 var self = this;
        1354
        1355 return this.execute(function() {
        1356 var startTime = goog.now();
        1357 var waitResult = new webdriver.promise.Deferred();
        1358 var waitFrame = self.activeFrame_;
        1359 waitFrame.isWaiting = true;
        1360 pollCondition();
        1361 return waitResult.promise;
        1362
        1363 function pollCondition() {
        1364 self.runInNewFrame_(condition, function(value) {
        1365 var elapsed = goog.now() - startTime;
        1366 if (!!value) {
        1367 waitFrame.isWaiting = false;
        1368 waitResult.fulfill(value);
        1369 } else if (elapsed >= timeout) {
        1370 waitResult.reject(new Error((opt_message ? opt_message + '\n' : '') +
        1371 'Wait timed out after ' + elapsed + 'ms'));
        1372 } else {
        1373 self.timer.setTimeout(pollCondition, sleep);
        1374 }
        1375 }, waitResult.reject, true);
        1376 }
        1377 }, opt_message);
        1378};
        1379
        1380
        1381/**
        1382 * Schedules a task that will wait for another promise to resolve. The resolved
        1383 * promise's value will be returned as the task result.
        1384 * @param {!webdriver.promise.Promise} promise The promise to wait on.
        1385 * @return {!webdriver.promise.Promise} A promise that will resolve when the
        1386 * task has completed.
        1387 */
        1388webdriver.promise.ControlFlow.prototype.await = function(promise) {
        1389 return this.execute(function() {
        1390 return promise;
        1391 });
        1392};
        1393
        1394
        1395/**
        1396 * Schedules the interval for this instance's event loop, if necessary.
        1397 * @private
        1398 */
        1399webdriver.promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() {
        1400 if (!this.eventLoopId_) {
        1401 this.eventLoopId_ = this.timer.setInterval(
        1402 goog.bind(this.runEventLoop_, this),
        1403 webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY);
        1404 }
        1405};
        1406
        1407
        1408/**
        1409 * Cancels the event loop, if necessary.
        1410 * @private
        1411 */
        1412webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() {
        1413 if (this.eventLoopId_) {
        1414 this.timer.clearInterval(this.eventLoopId_);
        1415 this.eventLoopId_ = null;
        1416 }
        1417};
        1418
        1419
        1420/**
        1421 * Executes the next task for the current frame. If the current frame has no
        1422 * more tasks, the frame's result will be resolved, returning control to the
        1423 * frame's creator. This will terminate the flow if the completed frame was at
        1424 * the top of the stack.
        1425 * @private
        1426 */
        1427webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() {
        1428 // If we get here and there are pending promise rejections, then those
        1429 // promises are queued up to run as soon as this (JS) event loop terminates.
        1430 // Short-circuit our loop to give those promises a chance to run. Otherwise,
        1431 // we might start a new task only to have it fail because of one of these
        1432 // pending rejections.
        1433 if (this.pendingRejections_) {
        1434 return;
        1435 }
        1436
        1437 // If the flow aborts due to an unhandled exception after we've scheduled
        1438 // another turn of the execution loop, we can end up in here with no tasks
        1439 // left. This is OK, just quietly return.
        1440 if (!this.activeFrame_) {
        1441 this.commenceShutdown_();
        1442 return;
        1443 }
        1444
        1445 var task;
        1446 if (this.activeFrame_.getPendingTask() || !(task = this.getNextTask_())) {
        1447 // Either the current frame is blocked on a pending task, or we don't have
        1448 // a task to finish because we've completed a frame. When completing a
        1449 // frame, we must abort the event loop to allow the frame's promise's
        1450 // callbacks to execute.
        1451 return;
        1452 }
        1453
        1454 var activeFrame = this.activeFrame_;
        1455 activeFrame.setPendingTask(task);
        1456 var markTaskComplete = goog.bind(function() {
        1457 this.history_.push(/** @type {!webdriver.promise.Task_} */ (task));
        1458 activeFrame.setPendingTask(null);
        1459 }, this);
        1460
        1461 this.trimHistory_();
        1462 var self = this;
        1463 this.runInNewFrame_(task.execute, function(result) {
        1464 markTaskComplete();
        1465 task.fulfill(result);
        1466 }, function(error) {
        1467 markTaskComplete();
        1468
        1469 if (!webdriver.promise.isError_(error) &&
        1470 !webdriver.promise.isPromise(error)) {
        1471 error = Error(error);
        1472 }
        1473
        1474 task.reject(self.annotateError(/** @type {!Error} */ (error)));
        1475 }, true);
        1476};
        1477
        1478
        1479/**
        1480 * @return {webdriver.promise.Task_} The next task to execute, or
        1481 * {@code null} if a frame was resolved.
        1482 * @private
        1483 */
        1484webdriver.promise.ControlFlow.prototype.getNextTask_ = function() {
        1485 var firstChild = this.activeFrame_.getFirstChild();
        1486 if (!firstChild) {
        1487 if (!this.activeFrame_.isWaiting) {
        1488 this.resolveFrame_(this.activeFrame_);
        1489 }
        1490 return null;
        1491 }
        1492
        1493 if (firstChild instanceof webdriver.promise.Frame_) {
        1494 this.activeFrame_ = firstChild;
        1495 return this.getNextTask_();
        1496 }
        1497
        1498 firstChild.getParent().removeChild(firstChild);
        1499 return firstChild;
        1500};
        1501
        1502
        1503/**
        1504 * @param {!webdriver.promise.Frame_} frame The frame to resolve.
        1505 * @private
        1506 */
        1507webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) {
        1508 if (this.activeFrame_ === frame) {
        1509 // Frame parent is always another frame, but the compiler is not smart
        1510 // enough to recognize this.
        1511 this.activeFrame_ =
        1512 /** @type {webdriver.promise.Frame_} */ (frame.getParent());
        1513 }
        1514
        1515 if (frame.getParent()) {
        1516 frame.getParent().removeChild(frame);
        1517 }
        1518 this.trimHistory_();
        1519 frame.fulfill();
        1520
        1521 if (!this.activeFrame_) {
        1522 this.commenceShutdown_();
        1523 }
        1524};
        1525
        1526
        1527/**
        1528 * Aborts the current frame. The frame, and all of the tasks scheduled within it
        1529 * will be discarded. If this instance does not have an active frame, it will
        1530 * immediately terminate all execution.
        1531 * @param {*} error The reason the frame is being aborted; typically either
        1532 * an Error or string.
        1533 * @private
        1534 */
        1535webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) {
        1536 // Annotate the error value if it is Error-like.
        1537 if (webdriver.promise.isError_(error)) {
        1538 this.annotateError(/** @type {!Error} */ (error));
        1539 }
        1540 this.numAbortedFrames_++;
        1541
        1542 if (!this.activeFrame_) {
        1543 this.abortNow_(error);
        1544 return;
        1545 }
        1546
        1547 // Frame parent is always another frame, but the compiler is not smart
        1548 // enough to recognize this.
        1549 var parent = /** @type {webdriver.promise.Frame_} */ (
        1550 this.activeFrame_.getParent());
        1551 if (parent) {
        1552 parent.removeChild(this.activeFrame_);
        1553 }
        1554
        1555 var frame = this.activeFrame_;
        1556 this.activeFrame_ = parent;
        1557 frame.reject(error);
        1558};
        1559
        1560
        1561/**
        1562 * Executes a function in a new frame. If the function does not schedule any new
        1563 * tasks, the frame will be discarded and the function's result returned
        1564 * immediately. Otherwise, a promise will be returned. This promise will be
        1565 * resolved with the function's result once all of the tasks scheduled within
        1566 * the function have been completed. If the function's frame is aborted, the
        1567 * returned promise will be rejected.
        1568 *
        1569 * @param {!Function} fn The function to execute.
        1570 * @param {function(*)} callback The function to call with a successful result.
        1571 * @param {function(*)} errback The function to call if there is an error.
        1572 * @param {boolean=} opt_activate Whether the active frame should be updated to
        1573 * the newly created frame so tasks are treated as sub-tasks.
        1574 * @private
        1575 */
        1576webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function(
        1577 fn, callback, errback, opt_activate) {
        1578 var newFrame = new webdriver.promise.Frame_(this),
        1579 self = this,
        1580 oldFrame = this.activeFrame_;
        1581
        1582 try {
        1583 if (!this.activeFrame_) {
        1584 this.activeFrame_ = newFrame;
        1585 } else {
        1586 this.activeFrame_.addChild(newFrame);
        1587 }
        1588
        1589 // Activate the new frame to force tasks to be treated as sub-tasks of
        1590 // the parent frame.
        1591 if (opt_activate) {
        1592 this.activeFrame_ = newFrame;
        1593 }
        1594
        1595 try {
        1596 this.schedulingFrame_ = newFrame;
        1597 webdriver.promise.pushFlow_(this);
        1598 var result = fn();
        1599 } finally {
        1600 webdriver.promise.popFlow_();
        1601 this.schedulingFrame_ = null;
        1602 }
        1603 newFrame.lockFrame();
        1604
        1605 // If there was nothing scheduled in the new frame we can discard the
        1606 // frame and return immediately.
        1607 if (!newFrame.children_.length) {
        1608 removeNewFrame();
        1609 webdriver.promise.asap(result, callback, errback);
        1610 return;
        1611 }
        1612
        1613 newFrame.then(function() {
        1614 webdriver.promise.asap(result, callback, errback);
        1615 }, function(e) {
        1616 if (result instanceof webdriver.promise.Promise && result.isPending()) {
        1617 result.cancel(e);
        1618 e = result;
        1619 }
        1620 errback(e);
        1621 });
        1622 } catch (ex) {
        1623 removeNewFrame(new webdriver.promise.CanceledTaskError_(ex));
        1624 errback(ex);
        1625 }
        1626
        1627 /**
        1628 * @param {webdriver.promise.CanceledTaskError_=} opt_err If provided, the
        1629 * error that triggered the removal of this frame.
        1630 */
        1631 function removeNewFrame(opt_err) {
        1632 var parent = newFrame.getParent();
        1633 if (parent) {
        1634 parent.removeChild(newFrame);
        1635 }
        1636
        1637 if (opt_err) {
        1638 newFrame.cancelRemainingTasks(opt_err);
        1639 }
        1640 self.activeFrame_ = oldFrame;
        1641 }
        1642};
        1643
        1644
        1645/**
        1646 * Commences the shutdown sequence for this instance. After one turn of the
        1647 * event loop, this object will emit the
        1648 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event to signal
        1649 * listeners that it has completed. During this wait, if another task is
        1650 * scheduled, the shutdown will be aborted.
        1651 * @private
        1652 */
        1653webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() {
        1654 if (!this.shutdownId_) {
        1655 // Go ahead and stop the event loop now. If we're in here, then there are
        1656 // no more frames with tasks to execute. If we waited to cancel the event
        1657 // loop in our timeout below, the event loop could trigger *before* the
        1658 // timeout, generating an error from there being no frames.
        1659 // If #execute is called before the timeout below fires, it will cancel
        1660 // the timeout and restart the event loop.
        1661 this.cancelEventLoop_();
        1662
        1663 var self = this;
        1664 self.shutdownId_ = self.timer.setTimeout(function() {
        1665 self.shutdownId_ = null;
        1666 self.emit(webdriver.promise.ControlFlow.EventType.IDLE);
        1667 }, 0);
        1668 }
        1669};
        1670
        1671
        1672/**
        1673 * Cancels the shutdown sequence if it is currently scheduled.
        1674 * @private
        1675 */
        1676webdriver.promise.ControlFlow.prototype.cancelShutdown_ = function() {
        1677 if (this.shutdownId_) {
        1678 this.timer.clearTimeout(this.shutdownId_);
        1679 this.shutdownId_ = null;
        1680 }
        1681};
        1682
        1683
        1684/**
        1685 * Aborts this flow, abandoning all remaining tasks. If there are
        1686 * listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the
        1687 * offending {@code error}, otherwise, the {@code error} will be rethrown to the
        1688 * global error handler.
        1689 * @param {*} error Object describing the error that caused the flow to
        1690 * abort; usually either an Error or string value.
        1691 * @private
        1692 */
        1693webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) {
        1694 this.activeFrame_ = null;
        1695 this.cancelShutdown_();
        1696 this.cancelEventLoop_();
        1697
        1698 var listeners = this.listeners(
        1699 webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
        1700 if (!listeners.length) {
        1701 this.timer.setTimeout(function() {
        1702 throw error;
        1703 }, 0);
        1704 } else {
        1705 this.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION,
        1706 error);
        1707 }
        1708};
        1709
        1710
        1711
        1712/**
        1713 * A single node in an {@link webdriver.promise.ControlFlow}'s task tree.
        1714 * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs
        1715 * to.
        1716 * @constructor
        1717 * @extends {webdriver.promise.Deferred}
        1718 * @private
        1719 */
        1720webdriver.promise.Node_ = function(flow) {
        1721 webdriver.promise.Deferred.call(this, null, flow);
        1722};
        1723goog.inherits(webdriver.promise.Node_, webdriver.promise.Deferred);
        1724
        1725
        1726/**
        1727 * This node's parent.
        1728 * @private {webdriver.promise.Node_}
        1729 */
        1730webdriver.promise.Node_.prototype.parent_ = null;
        1731
        1732
        1733/** @return {webdriver.promise.Node_} This node's parent. */
        1734webdriver.promise.Node_.prototype.getParent = function() {
        1735 return this.parent_;
        1736};
        1737
        1738
        1739/**
        1740 * @param {webdriver.promise.Node_} parent This node's new parent.
        1741 */
        1742webdriver.promise.Node_.prototype.setParent = function(parent) {
        1743 this.parent_ = parent;
        1744};
        1745
        1746
        1747/**
        1748 * @return {!webdriver.promise.Node_} The root of this node's tree.
        1749 */
        1750webdriver.promise.Node_.prototype.getRoot = function() {
        1751 var root = this;
        1752 while (root.parent_) {
        1753 root = root.parent_;
        1754 }
        1755 return root;
        1756};
        1757
        1758
        1759
        1760/**
        1761 * An execution frame within a {@link webdriver.promise.ControlFlow}. Each
        1762 * frame represents the execution context for either a
        1763 * {@link webdriver.promise.Task_} or a callback on a
        1764 * {@link webdriver.promise.Deferred}.
        1765 *
        1766 * <p>Each frame may contain sub-frames. If child N is a sub-frame, then the
        1767 * items queued within it are given priority over child N+1.
        1768 *
        1769 * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs
        1770 * to.
        1771 * @constructor
        1772 * @extends {webdriver.promise.Node_}
        1773 * @private
        1774 */
        1775webdriver.promise.Frame_ = function(flow) {
        1776 webdriver.promise.Node_.call(this, flow);
        1777
        1778 var reject = goog.bind(this.reject, this);
        1779 var cancelRemainingTasks = goog.bind(this.cancelRemainingTasks, this);
        1780
        1781 /** @override */
        1782 this.reject = function(e) {
        1783 cancelRemainingTasks(new webdriver.promise.CanceledTaskError_(e));
        1784 reject(e);
        1785 };
        1786
        1787 /**
        1788 * @private {!Array.<!(webdriver.promise.Frame_|webdriver.promise.Task_)>}
        1789 */
        1790 this.children_ = [];
        1791};
        1792goog.inherits(webdriver.promise.Frame_, webdriver.promise.Node_);
        1793
        1794
        1795/**
        1796 * The task currently being executed within this frame.
        1797 * @private {webdriver.promise.Task_}
        1798 */
        1799webdriver.promise.Frame_.prototype.pendingTask_ = null;
        1800
        1801
        1802/**
        1803 * Whether this frame is active. A frame is considered active once one of its
        1804 * descendants has been removed for execution.
        1805 *
        1806 * Adding a sub-frame as a child to an active frame is an indication that
        1807 * a callback to a {@link webdriver.promise.Deferred} is being invoked and any
        1808 * tasks scheduled within it should have priority over previously scheduled
        1809 * tasks:
        1810 * <code><pre>
        1811 * var flow = webdriver.promise.controlFlow();
        1812 * flow.execute('start here', goog.nullFunction).then(function() {
        1813 * flow.execute('this should execute 2nd', goog.nullFunction);
        1814 * });
        1815 * flow.execute('this should execute last', goog.nullFunction);
        1816 * </pre></code>
        1817 *
        1818 * @private {boolean}
        1819 */
        1820webdriver.promise.Frame_.prototype.isActive_ = false;
        1821
        1822
        1823/**
        1824 * Whether this frame is currently locked. A locked frame represents a callback
        1825 * or task function which has run to completion and scheduled all of its tasks.
        1826 *
        1827 * <p>Once a frame becomes {@link #isActive_ active}, any new frames which are
        1828 * added represent callbacks on a {@link webdriver.promise.Deferred}, whose
        1829 * tasks must be given priority over previously scheduled tasks.
        1830 *
        1831 * @private {boolean}
        1832 */
        1833webdriver.promise.Frame_.prototype.isLocked_ = false;
        1834
        1835
        1836/**
        1837 * A reference to the last node inserted in this frame.
        1838 * @private {webdriver.promise.Node_}
        1839 */
        1840webdriver.promise.Frame_.prototype.lastInsertedChild_ = null;
        1841
        1842
        1843/**
        1844 * Marks all of the tasks that are descendants of this frame in the execution
        1845 * tree as cancelled. This is necessary for callbacks scheduled asynchronous.
        1846 * For example:
        1847 *
        1848 * var someResult;
        1849 * webdriver.promise.createFlow(function(flow) {
        1850 * someResult = flow.execute(function() {});
        1851 * throw Error();
        1852 * }).addErrback(function(err) {
        1853 * console.log('flow failed: ' + err);
        1854 * someResult.then(function() {
        1855 * console.log('task succeeded!');
        1856 * }, function(err) {
        1857 * console.log('task failed! ' + err);
        1858 * });
        1859 * });
        1860 * // flow failed: Error: boom
        1861 * // task failed! CanceledTaskError: Task discarded due to a previous
        1862 * // task failure: Error: boom
        1863 *
        1864 * @param {!webdriver.promise.CanceledTaskError_} error The cancellation
        1865 * error.
        1866 */
        1867webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(error) {
        1868 goog.array.forEach(this.children_, function(child) {
        1869 if (child instanceof webdriver.promise.Frame_) {
        1870 child.cancelRemainingTasks(error);
        1871 } else {
        1872 // None of the previously registered listeners should be notified that
        1873 // the task is being canceled, however, we need at least one errback
        1874 // to prevent the cancellation from bubbling up.
        1875 child.removeAll();
        1876 child.thenCatch(goog.nullFunction);
        1877 child.cancel(error);
        1878 }
        1879 });
        1880};
        1881
        1882
        1883/**
        1884 * @return {webdriver.promise.Task_} The task currently executing
        1885 * within this frame, if any.
        1886 */
        1887webdriver.promise.Frame_.prototype.getPendingTask = function() {
        1888 return this.pendingTask_;
        1889};
        1890
        1891
        1892/**
        1893 * @param {webdriver.promise.Task_} task The task currently
        1894 * executing within this frame, if any.
        1895 */
        1896webdriver.promise.Frame_.prototype.setPendingTask = function(task) {
        1897 this.pendingTask_ = task;
        1898};
        1899
        1900
        1901/** Locks this frame. */
        1902webdriver.promise.Frame_.prototype.lockFrame = function() {
        1903 this.isLocked_ = true;
        1904};
        1905
        1906
        1907/**
        1908 * Adds a new node to this frame.
        1909 * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node
        1910 * The node to insert.
        1911 */
        1912webdriver.promise.Frame_.prototype.addChild = function(node) {
        1913 if (this.lastInsertedChild_ &&
        1914 this.lastInsertedChild_ instanceof webdriver.promise.Frame_ &&
        1915 !this.lastInsertedChild_.isLocked_) {
        1916 this.lastInsertedChild_.addChild(node);
        1917 return;
        1918 }
        1919
        1920 node.setParent(this);
        1921
        1922 if (this.isActive_ && node instanceof webdriver.promise.Frame_) {
        1923 var index = 0;
        1924 if (this.lastInsertedChild_ instanceof
        1925 webdriver.promise.Frame_) {
        1926 index = goog.array.indexOf(this.children_, this.lastInsertedChild_) + 1;
        1927 }
        1928 goog.array.insertAt(this.children_, node, index);
        1929 this.lastInsertedChild_ = node;
        1930 return;
        1931 }
        1932
        1933 this.lastInsertedChild_ = node;
        1934 this.children_.push(node);
        1935};
        1936
        1937
        1938/**
        1939 * @return {(webdriver.promise.Frame_|webdriver.promise.Task_)} This frame's
        1940 * fist child.
        1941 */
        1942webdriver.promise.Frame_.prototype.getFirstChild = function() {
        1943 this.isActive_ = true;
        1944 this.lastInsertedChild_ = null;
        1945 return this.children_[0];
        1946};
        1947
        1948
        1949/**
        1950 * Removes a child from this frame.
        1951 * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} child
        1952 * The child to remove.
        1953 */
        1954webdriver.promise.Frame_.prototype.removeChild = function(child) {
        1955 var index = goog.array.indexOf(this.children_, child);
        1956 child.setParent(null);
        1957 goog.array.removeAt(this.children_, index);
        1958 if (this.lastInsertedChild_ === child) {
        1959 this.lastInsertedChild_ = null;
        1960 }
        1961};
        1962
        1963
        1964/** @override */
        1965webdriver.promise.Frame_.prototype.toString = function() {
        1966 return '[' + goog.array.map(this.children_, function(child) {
        1967 return child.toString();
        1968 }).join(', ') + ']';
        1969};
        1970
        1971
        1972
        1973/**
        1974 * A task to be executed by a {@link webdriver.promise.ControlFlow}.
        1975 *
        1976 * @param {!webdriver.promise.ControlFlow} flow The flow this instances belongs
        1977 * to.
        1978 * @param {!Function} fn The function to call when the task executes. If it
        1979 * returns a {@code webdriver.promise.Promise}, the flow will wait
        1980 * for it to be resolved before starting the next task.
        1981 * @param {string} description A description of the task for debugging.
        1982 * @param {!webdriver.stacktrace.Snapshot} snapshot A snapshot of the stack
        1983 * when this task was scheduled.
        1984 * @constructor
        1985 * @extends {webdriver.promise.Node_}
        1986 * @private
        1987 */
        1988webdriver.promise.Task_ = function(flow, fn, description, snapshot) {
        1989 webdriver.promise.Node_.call(this, flow);
        1990
        1991 /**
        1992 * Executes this task.
        1993 * @type {!Function}
        1994 */
        1995 this.execute = fn;
        1996
        1997 /** @private {string} */
        1998 this.description_ = description;
        1999
        2000 /** @private {!webdriver.stacktrace.Snapshot} */
        2001 this.snapshot_ = snapshot;
        2002};
        2003goog.inherits(webdriver.promise.Task_, webdriver.promise.Node_);
        2004
        2005
        2006/** @return {string} This task's description. */
        2007webdriver.promise.Task_.prototype.getDescription = function() {
        2008 return this.description_;
        2009};
        2010
        2011
        2012/** @override */
        2013webdriver.promise.Task_.prototype.toString = function() {
        2014 var stack = this.snapshot_.getStacktrace();
        2015 var ret = this.description_;
        2016 if (stack.length) {
        2017 if (this.description_) {
        2018 ret += '\n';
        2019 }
        2020 ret += stack.join('\n');
        2021 }
        2022 return ret;
        2023};
        2024
        2025
        2026
        2027/**
        2028 * Special error used to signal when a task is canceled because a previous
        2029 * task in the same frame failed.
        2030 * @param {*} err The error that caused the task cancellation.
        2031 * @constructor
        2032 * @extends {goog.debug.Error}
        2033 * @private
        2034 */
        2035webdriver.promise.CanceledTaskError_ = function(err) {
        2036 goog.base(this, 'Task discarded due to a previous task failure: ' + err);
        2037};
        2038goog.inherits(webdriver.promise.CanceledTaskError_, goog.debug.Error);
        2039
        2040
        2041/** @override */
        2042webdriver.promise.CanceledTaskError_.prototype.name = 'CanceledTaskError';
        2043
        2044
        2045
        2046/**
        2047 * The default flow to use if no others are active.
        2048 * @private {!webdriver.promise.ControlFlow}
        2049 */
        2050webdriver.promise.defaultFlow_ = new webdriver.promise.ControlFlow();
        2051
        2052
        2053/**
        2054 * A stack of active control flows, with the top of the stack used to schedule
        2055 * commands. When there are multiple flows on the stack, the flow at index N
        2056 * represents a callback triggered within a task owned by the flow at index
        2057 * N-1.
        2058 * @private {!Array.<!webdriver.promise.ControlFlow>}
        2059 */
        2060webdriver.promise.activeFlows_ = [];
        2061
        2062
        2063/**
        2064 * Changes the default flow to use when no others are active.
        2065 * @param {!webdriver.promise.ControlFlow} flow The new default flow.
        2066 * @throws {Error} If the default flow is not currently active.
        2067 */
        2068webdriver.promise.setDefaultFlow = function(flow) {
        2069 if (webdriver.promise.activeFlows_.length) {
        2070 throw Error('You may only change the default flow while it is active');
        2071 }
        2072 webdriver.promise.defaultFlow_ = flow;
        2073};
        2074
        2075
        2076/**
        2077 * @return {!webdriver.promise.ControlFlow} The currently active control flow.
        2078 */
        2079webdriver.promise.controlFlow = function() {
        2080 return /** @type {!webdriver.promise.ControlFlow} */ (
        2081 goog.array.peek(webdriver.promise.activeFlows_) ||
        2082 webdriver.promise.defaultFlow_);
        2083};
        2084
        2085
        2086/**
        2087 * @param {!webdriver.promise.ControlFlow} flow The new flow.
        2088 * @private
        2089 */
        2090webdriver.promise.pushFlow_ = function(flow) {
        2091 webdriver.promise.activeFlows_.push(flow);
        2092};
        2093
        2094
        2095/** @private */
        2096webdriver.promise.popFlow_ = function() {
        2097 webdriver.promise.activeFlows_.pop();
        2098};
        2099
        2100
        2101/**
        2102 * Creates a new control flow. The provided callback will be invoked as the
        2103 * first task within the new flow, with the flow as its sole argument. Returns
        2104 * a promise that resolves to the callback result.
        2105 * @param {function(!webdriver.promise.ControlFlow)} callback The entry point
        2106 * to the newly created flow.
        2107 * @return {!webdriver.promise.Promise} A promise that resolves to the callback
        2108 * result.
        2109 */
        2110webdriver.promise.createFlow = function(callback) {
        2111 var flow = new webdriver.promise.ControlFlow(
        2112 webdriver.promise.defaultFlow_.timer);
        2113 return flow.execute(function() {
        2114 return callback(flow);
        2115 });
        2116};
        \ No newline at end of file +promise.js

        lib/webdriver/promise.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @license Portions of this code are from the Dojo toolkit, received under the
        20 * BSD License:
        21 * Redistribution and use in source and binary forms, with or without
        22 * modification, are permitted provided that the following conditions are met:
        23 *
        24 * * Redistributions of source code must retain the above copyright notice,
        25 * this list of conditions and the following disclaimer.
        26 * * Redistributions in binary form must reproduce the above copyright notice,
        27 * this list of conditions and the following disclaimer in the documentation
        28 * and/or other materials provided with the distribution.
        29 * * Neither the name of the Dojo Foundation nor the names of its contributors
        30 * may be used to endorse or promote products derived from this software
        31 * without specific prior written permission.
        32 *
        33 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
        34 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        36 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
        37 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        38 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
        39 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
        40 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
        41 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        42 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        43 * POSSIBILITY OF SUCH DAMAGE.
        44 */
        45
        46/**
        47 * @fileoverview
        48 * The promise module is centered around the
        49 * {@linkplain webdriver.promise.ControlFlow ControlFlow}, a class that
        50 * coordinates the execution of asynchronous tasks. The ControlFlow allows users
        51 * to focus on the imperative commands for their script without worrying about
        52 * chaining together every single asynchronous action, which can be tedious and
        53 * verbose. APIs may be layered on top of the control flow to read as if they
        54 * were synchronous. For instance, the core
        55 * {@linkplain webdriver.WebDriver WebDriver} API is built on top of the
        56 * control flow, allowing users to write
        57 *
        58 * driver.get('http://www.google.com/ncr');
        59 * driver.findElement({name: 'q'}).sendKeys('webdriver');
        60 * driver.findElement({name: 'btnGn'}).click();
        61 *
        62 * instead of
        63 *
        64 * driver.get('http://www.google.com/ncr')
        65 * .then(function() {
        66 * return driver.findElement({name: 'q'});
        67 * })
        68 * .then(function(q) {
        69 * return q.sendKeys('webdriver');
        70 * })
        71 * .then(function() {
        72 * return driver.findElement({name: 'btnG'});
        73 * })
        74 * .then(function(btnG) {
        75 * return btnG.click();
        76 * });
        77 *
        78 * ## Tasks and Task Queues
        79 *
        80 * The control flow is based on the concept of tasks and task queues. Tasks are
        81 * functions that define the basic unit of work for the control flow to execute.
        82 * Each task is scheduled via
        83 * {@link webdriver.promise.ControlFlow#execute() ControlFlow#execute()}, which
        84 * will return a {@link webdriver.promise.Promise Promise} that will be resolved
        85 * with the task's result.
        86 *
        87 * A task queue contains all of the tasks scheduled within a single turn of the
        88 * [JavaScript event loop][JSEL]. The control flow will create a new task queue
        89 * the first time a task is scheduled within an event loop.
        90 *
        91 * var flow = promise.controlFlow();
        92 * flow.execute(foo); // Creates a new task queue and inserts foo.
        93 * flow.execute(bar); // Inserts bar into the same queue as foo.
        94 * setTimeout(function() {
        95 * flow.execute(baz); // Creates a new task queue and inserts baz.
        96 * }, 0);
        97 *
        98 * Whenever the control flow creates a new task queue, it will automatically
        99 * begin executing tasks in the next available turn of the event loop. This
        100 * execution is scheduled using a "micro-task" timer, such as a (native)
        101 * `Promise.then()` callback.
        102 *
        103 * setTimeout(() => console.log('a'));
        104 * Promise.resolve().then(() => console.log('b')); // A native promise.
        105 * flow.execute(() => console.log('c'));
        106 * Promise.resolve().then(() => console.log('d'));
        107 * setTimeout(() => console.log('fin'));
        108 * // b
        109 * // c
        110 * // d
        111 * // a
        112 * // fin
        113 *
        114 * In the example above, b/c/d is logged before a/fin because native promises
        115 * and this module use "micro-task" timers, which have a higher priority than
        116 * "macro-tasks" like `setTimeout`.
        117 *
        118 * ## Task Execution
        119 *
        120 * Upon creating a task queue, and whenever an exisiting queue completes a task,
        121 * the control flow will schedule a micro-task timer to process any scheduled
        122 * tasks. This ensures no task is ever started within the same turn of the
        123 * JavaScript event loop in which it was scheduled, nor is a task ever started
        124 * within the same turn that another finishes.
        125 *
        126 * When the execution timer fires, a single task will be dequeued and executed.
        127 * There are several important events that may occur while executing a task
        128 * function:
        129 *
        130 * 1. A new task queue is created by a call to
        131 * {@link webdriver.promise.ControlFlow#execute ControlFlow#execute()}. Any
        132 * tasks scheduled within this task queue are considered subtasks of the
        133 * current task.
        134 * 2. The task function throws an error. Any scheduled tasks are immediately
        135 * discarded and the task's promised result (previously returned by
        136 * {@link webdriver.promise.ControlFlow#execute ControlFlow#execute()}) is
        137 * immediately rejected with the thrown error.
        138 * 3. The task function returns sucessfully.
        139 *
        140 * If a task function created a new task queue, the control flow will wait for
        141 * that queue to complete before processing the task result. If the queue
        142 * completes without error, the flow will settle the task's promise with the
        143 * value originaly returned by the task function. On the other hand, if the task
        144 * queue termintes with an error, the task's promise will be rejected with that
        145 * error.
        146 *
        147 * flow.execute(function() {
        148 * flow.execute(() => console.log('a'));
        149 * flow.execute(() => console.log('b'));
        150 * });
        151 * flow.execute(() => console.log('c'));
        152 * // a
        153 * // b
        154 * // c
        155 *
        156 * ## Promise Integration
        157 *
        158 * In addition to the {@link webdriver.promise.ControlFlow ControlFlow} class,
        159 * the promise module also exports a [Promise/A+]
        160 * {@linkplain webdriver.promise.Promise implementation} that is deeply
        161 * integrated with the ControlFlow. First and foremost, each promise
        162 * {@linkplain webdriver.promise.Promise#then() callback} is scheduled with the
        163 * control flow as a task. As a result, each callback is invoked in its own turn
        164 * of the JavaScript event loop with its own task queue. If any tasks are
        165 * scheduled within a callback, the callback's promised result will not be
        166 * settled until the task queue has completed.
        167 *
        168 * promise.fulfilled().then(function() {
        169 * flow.execute(function() {
        170 * console.log('b');
        171 * });
        172 * }).then(() => console.log('a'));
        173 * // b
        174 * // a
        175 *
        176 * ### Scheduling Promise Callbacks <a id="scheduling_callbacks"></a>
        177 *
        178 * How callbacks are scheduled in the control flow depends on when they are
        179 * attached to the promise. Callbacks attached to a _previously_ resolved
        180 * promise are immediately enqueued as subtasks of the currently running task.
        181 *
        182 * var p = promise.fulfilled();
        183 * flow.execute(function() {
        184 * flow.execute(() => console.log('A'));
        185 * p.then( () => console.log('B'));
        186 * flow.execute(() => console.log('C'));
        187 * p.then( () => console.log('D'));
        188 * }).then(function() {
        189 * console.log('fin');
        190 * });
        191 * // A
        192 * // B
        193 * // C
        194 * // D
        195 * // fin
        196 *
        197 * When a promise is resolved while a task function is on the call stack, any
        198 * callbacks also registered in that stack frame are scheduled as if the promise
        199 * were already resolved:
        200 *
        201 * var d = promise.defer();
        202 * flow.execute(function() {
        203 * flow.execute( () => console.log('A'));
        204 * d.promise.then(() => console.log('B'));
        205 * flow.execute( () => console.log('C'));
        206 * d.promise.then(() => console.log('D'));
        207 *
        208 * d.fulfill();
        209 * }).then(function() {
        210 * console.log('fin');
        211 * });
        212 * // A
        213 * // B
        214 * // C
        215 * // D
        216 * // fin
        217 *
        218 * If a promise is resolved while a task function is on the call stack, any
        219 * previously registered callbacks (i.e. attached while the task was _not_ on
        220 * the call stack), act as _interrupts_ and are inserted at the front of the
        221 * task queue. If multiple promises are fulfilled, their interrupts are enqueued
        222 * in the order the promises are resolved.
        223 *
        224 * var d1 = promise.defer();
        225 * d1.promise.then(() => console.log('A'));
        226 *
        227 * var d2 = promise.defer();
        228 * d2.promise.then(() => console.log('B'));
        229 *
        230 * flow.execute(function() {
        231 * flow.execute(() => console.log('C'));
        232 * flow.execute(() => console.log('D'));
        233 * d1.fulfill();
        234 * d2.fulfill();
        235 * }).then(function() {
        236 * console.log('fin');
        237 * });
        238 * // A
        239 * // B
        240 * // C
        241 * // D
        242 * // fin
        243 *
        244 * Within a task function (or callback), each step of a promise chain acts as
        245 * an interrupt on the task queue:
        246 *
        247 * var d = promise.defer();
        248 * flow.execute(function() {
        249 * d.promise.
        250 * then(() => console.log('A')).
        251 * then(() => console.log('B')).
        252 * then(() => console.log('C')).
        253 * then(() => console.log('D'));
        254 *
        255 * flow.execute(() => console.log('E'));
        256 * d.fulfill();
        257 * }).then(function() {
        258 * console.log('fin');
        259 * });
        260 * // A
        261 * // B
        262 * // C
        263 * // D
        264 * // E
        265 * // fin
        266 *
        267 * If there are multiple promise chains derived from a single promise, they are
        268 * processed in the order created:
        269 *
        270 * var d = promise.defer();
        271 * flow.execute(function() {
        272 * var chain = d.promise.then(() => console.log('A'));
        273 *
        274 * chain.then(() => console.log('B')).
        275 * then(() => console.log('C'));
        276 *
        277 * chain.then(() => console.log('D')).
        278 * then(() => console.log('E'));
        279 *
        280 * flow.execute(() => console.log('F'));
        281 *
        282 * d.fulfill();
        283 * }).then(function() {
        284 * console.log('fin');
        285 * });
        286 * // A
        287 * // B
        288 * // C
        289 * // D
        290 * // E
        291 * // F
        292 * // fin
        293 *
        294 * Even though a subtask's promised result will never resolve while the task
        295 * function is on the stack, it will be treated as a promise resolved within the
        296 * task. In all other scenarios, a task's promise behaves just like a normal
        297 * promise. In the sample below, `C/D` is loggged before `B` because the
        298 * resolution of `subtask1` interrupts the flow of the enclosing task. Within
        299 * the final subtask, `E/F` is logged in order because `subtask1` is a resolved
        300 * promise when that task runs.
        301 *
        302 * flow.execute(function() {
        303 * var subtask1 = flow.execute(() => console.log('A'));
        304 * var subtask2 = flow.execute(() => console.log('B'));
        305 *
        306 * subtask1.then(() => console.log('C'));
        307 * subtask1.then(() => console.log('D'));
        308 *
        309 * flow.execute(function() {
        310 * flow.execute(() => console.log('E'));
        311 * subtask1.then(() => console.log('F'));
        312 * });
        313 * }).then(function() {
        314 * console.log('fin');
        315 * });
        316 * // A
        317 * // C
        318 * // D
        319 * // B
        320 * // E
        321 * // F
        322 * // fin
        323 *
        324 * __Note__: while the ControlFlow will wait for
        325 * {@linkplain webdriver.promise.ControlFlow#execute tasks} and
        326 * {@linkplain webdriver.promise.Promise#then callbacks} to complete, it
        327 * _will not_ wait for unresolved promises created within a task:
        328 *
        329 * flow.execute(function() {
        330 * var p = new promise.Promise(function(fulfill) {
        331 * setTimeout(fulfill, 100);
        332 * });
        333 *
        334 * p.then(() => console.log('promise resolved!'));
        335 *
        336 * }).then(function() {
        337 * console.log('task complete!');
        338 * });
        339 * // task complete!
        340 * // promise resolved!
        341 *
        342 * Finally, consider the following:
        343 *
        344 * var d = promise.defer();
        345 * d.promise.then(() => console.log('A'));
        346 * d.promise.then(() => console.log('B'));
        347 *
        348 * flow.execute(function() {
        349 * flow.execute( () => console.log('C'));
        350 * d.promise.then(() => console.log('D'));
        351 *
        352 * flow.execute( () => console.log('E'));
        353 * d.promise.then(() => console.log('F'));
        354 *
        355 * d.fulfill();
        356 *
        357 * flow.execute( () => console.log('G'));
        358 * d.promise.then(() => console.log('H'));
        359 * }).then(function() {
        360 * console.log('fin');
        361 * });
        362 * // A
        363 * // B
        364 * // C
        365 * // D
        366 * // E
        367 * // F
        368 * // G
        369 * // H
        370 * // fin
        371 *
        372 * In this example, callbacks are registered on `d.promise` both before and
        373 * during the invocation of the task function. When `d.fulfill()` is called,
        374 * the callbacks registered before the task (`A` & `B`) are registered as
        375 * interrupts. The remaining callbacks were all attached within the task and
        376 * are scheduled in the flow as standard tasks.
        377 *
        378 * ## Generator Support
        379 *
        380 * [Generators][GF] may be scheduled as tasks within a control flow or attached
        381 * as callbacks to a promise. Each time the generator yields a promise, the
        382 * control flow will wait for that promise to settle before executing the next
        383 * iteration of the generator. The yielded promise's fulfilled value will be
        384 * passed back into the generator:
        385 *
        386 * flow.execute(function* () {
        387 * var d = promise.defer();
        388 *
        389 * setTimeout(() => console.log('...waiting...'), 25);
        390 * setTimeout(() => d.fulfill(123), 50);
        391 *
        392 * console.log('start: ' + Date.now());
        393 *
        394 * var value = yield d.promise;
        395 * console.log('mid: %d; value = %d', Date.now(), value);
        396 *
        397 * yield promise.delayed(10);
        398 * console.log('end: ' + Date.now());
        399 * }).then(function() {
        400 * console.log('fin');
        401 * });
        402 * // start: 0
        403 * // ...waiting...
        404 * // mid: 50; value = 123
        405 * // end: 60
        406 * // fin
        407 *
        408 * Yielding the result of a promise chain will wait for the entire chain to
        409 * complete:
        410 *
        411 * promise.fulfilled().then(function* () {
        412 * console.log('start: ' + Date.now());
        413 *
        414 * var value = yield flow.
        415 * execute(() => console.log('A')).
        416 * then( () => console.log('B')).
        417 * then( () => 123);
        418 *
        419 * console.log('mid: %s; value = %d', Date.now(), value);
        420 *
        421 * yield flow.execute(() => console.log('C'));
        422 * }).then(function() {
        423 * console.log('fin');
        424 * });
        425 * // start: 0
        426 * // A
        427 * // B
        428 * // mid: 2; value = 123
        429 * // C
        430 * // fin
        431 *
        432 * Yielding a _rejected_ promise will cause the rejected value to be thrown
        433 * within the generator function:
        434 *
        435 * flow.execute(function* () {
        436 * console.log('start: ' + Date.now());
        437 * try {
        438 * yield promise.delayed(10).then(function() {
        439 * throw Error('boom');
        440 * });
        441 * } catch (ex) {
        442 * console.log('caught time: ' + Date.now());
        443 * console.log(ex.message);
        444 * }
        445 * });
        446 * // start: 0
        447 * // caught time: 10
        448 * // boom
        449 *
        450 * # Error Handling
        451 *
        452 * ES6 promises do not require users to handle a promise rejections. This can
        453 * result in subtle bugs as the rejections are silently "swallowed" by the
        454 * Promise class.
        455 *
        456 * Promise.reject(Error('boom'));
        457 * // ... *crickets* ...
        458 *
        459 * Selenium's {@link webdriver.promise promise} module, on the other hand,
        460 * requires that every rejection be explicitly handled. When a
        461 * {@linkplain webdriver.promise.Promise Promise} is rejected and no callbacks
        462 * are defined on that promise, it is considered an _unhandled rejection_ and
        463 * reproted to the active task queue. If the rejection remains unhandled after
        464 * a single turn of the [event loop][JSEL] (scheduled with a micro-task), it
        465 * will propagate up the stack.
        466 *
        467 * ## Error Propagation
        468 *
        469 * If an unhandled rejection occurs within a task function, that task's promised
        470 * result is rejected and all remaining subtasks are discarded:
        471 *
        472 * flow.execute(function() {
        473 * // No callbacks registered on promise -> unhandled rejection
        474 * promise.rejected(Error('boom'));
        475 * flow.execute(function() { console.log('this will never run'); });
        476 * }).thenCatch(function(e) {
        477 * console.log(e.message);
        478 * });
        479 * // boom
        480 *
        481 * The promised results for discarded tasks are silently rejected with a
        482 * cancellation error and existing callback chains will never fire.
        483 *
        484 * flow.execute(function() {
        485 * promise.rejected(Error('boom'));
        486 * flow.execute(function() { console.log('a'); }).
        487 * then(function() { console.log('b'); });
        488 * }).thenCatch(function(e) {
        489 * console.log(e.message);
        490 * });
        491 * // boom
        492 *
        493 * An unhandled rejection takes precedence over a task function's returned
        494 * result, even if that value is another promise:
        495 *
        496 * flow.execute(function() {
        497 * promise.rejected(Error('boom'));
        498 * return flow.execute(someOtherTask);
        499 * }).thenCatch(function(e) {
        500 * console.log(e.message);
        501 * });
        502 * // boom
        503 *
        504 * If there are multiple unhandled rejections within a task, they are packaged
        505 * in a {@link webdriver.promise.MultipleUnhandledRejectionError
        506 * MultipleUnhandledRejectionError}, which has an `errors` property that is a
        507 * `Set` of the recorded unhandled rejections:
        508 *
        509 * flow.execute(function() {
        510 * promise.rejected(Error('boom1'));
        511 * promise.rejected(Error('boom2'));
        512 * }).thenCatch(function(ex) {
        513 * console.log(ex instanceof promise.MultipleUnhandledRejectionError);
        514 * for (var e of ex.errors) {
        515 * console.log(e.message);
        516 * }
        517 * });
        518 * // boom1
        519 * // boom2
        520 *
        521 * When a subtask is discarded due to an unreported rejection in its parent
        522 * frame, the existing callbacks on that task will never settle and the
        523 * callbacks will not be invoked. If a new callback is attached ot the subtask
        524 * _after_ it has been discarded, it is handled the same as adding a callback
        525 * to a cancelled promise: the error-callback path is invoked. This behavior is
        526 * intended to handle cases where the user saves a reference to a task promise,
        527 * as illustrated below.
        528 *
        529 * var subTask;
        530 * flow.execute(function() {
        531 * promise.rejected(Error('boom'));
        532 * subTask = flow.execute(function() {});
        533 * }).thenCatch(function(e) {
        534 * console.log(e.message);
        535 * }).then(function() {
        536 * return subTask.then(
        537 * () => console.log('subtask success!'),
        538 * (e) => console.log('subtask failed:\n' + e));
        539 * });
        540 * // boom
        541 * // subtask failed:
        542 * // DiscardedTaskError: Task was discarded due to a previous failure: boom
        543 *
        544 * When a subtask fails, its promised result is treated the same as any other
        545 * promise: it must be handled within one turn of the rejection or the unhandled
        546 * rejection is propagated to the parent task. This means users can catch errors
        547 * from complex flows from the top level task:
        548 *
        549 * flow.execute(function() {
        550 * flow.execute(function() {
        551 * flow.execute(function() {
        552 * throw Error('fail!');
        553 * });
        554 * });
        555 * }).thenCatch(function(e) {
        556 * console.log(e.message);
        557 * });
        558 * // fail!
        559 *
        560 * ## Unhandled Rejection Events
        561 *
        562 * When an unhandled rejection propagates to the root of the control flow, the
        563 * flow will emit an __uncaughtException__ event. If no listeners are registered
        564 * on the flow, the error will be rethrown to the global error handler: an
        565 * __uncaughtException__ event from the
        566 * [`process`](https://nodejs.org/api/process.html) object in node, or
        567 * `window.onerror` when running in a browser.
        568 *
        569 * Bottom line: you __*must*__ handle rejected promises.
        570 *
        571 * # Promise/A+ Compatibility
        572 *
        573 * This `promise` module is compliant with the [Promise/A+][] specification
        574 * except for sections `2.2.6.1` and `2.2.6.2`:
        575 *
        576 * >
        577 * > - `then` may be called multiple times on the same promise.
        578 * > - If/when `promise` is fulfilled, all respective `onFulfilled` callbacks
        579 * > must execute in the order of their originating calls to `then`.
        580 * > - If/when `promise` is rejected, all respective `onRejected` callbacks
        581 * > must execute in the order of their originating calls to `then`.
        582 * >
        583 *
        584 * Specifically, the conformance tests contains the following scenario (for
        585 * brevity, only the fulfillment version is shown):
        586 *
        587 * var p1 = Promise.resolve();
        588 * p1.then(function() {
        589 * console.log('A');
        590 * p1.then(() => console.log('B'));
        591 * });
        592 * p1.then(() => console.log('C'));
        593 * // A
        594 * // C
        595 * // B
        596 *
        597 * Since the [ControlFlow](#scheduling_callbacks) executes promise callbacks as
        598 * tasks, with this module, the result would be
        599 *
        600 * var p2 = promise.fulfilled();
        601 * p2.then(function() {
        602 * console.log('A');
        603 * p2.then(() => console.log('B');
        604 * });
        605 * p2.then(() => console.log('C'));
        606 * // A
        607 * // B
        608 * // C
        609 *
        610 * [JSEL]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
        611 * [GF]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
        612 * [Promise/A+]: https://promisesaplus.com/
        613 */
        614
        615goog.module('webdriver.promise');
        616goog.module.declareLegacyNamespace();
        617
        618var Arrays = goog.require('goog.array');
        619var asserts = goog.require('goog.asserts');
        620var asyncRun = goog.require('goog.async.run');
        621var throwException = goog.require('goog.async.throwException');
        622var DebugError = goog.require('goog.debug.Error');
        623var log = goog.require('goog.log');
        624var Objects = goog.require('goog.object');
        625var EventEmitter = goog.require('webdriver.EventEmitter');
        626var stacktrace = goog.require('webdriver.stacktrace');
        627
        628
        629
        630/**
        631 * @define {boolean} Whether to append traces of {@code then} to rejection
        632 * errors.
        633 */
        634goog.define('webdriver.promise.LONG_STACK_TRACES', false);
        635
        636/** @const */
        637var promise = exports;
        638
        639
        640/** @const */
        641var LOG = log.getLogger('webdriver.promise');
        642
        643
        644/**
        645 * @param {number} level What level of verbosity to log with.
        646 * @param {(string|function(this: T): string)} loggable The message to log.
        647 * @param {T=} opt_self The object in whose context to run the loggable
        648 * function.
        649 * @template T
        650 */
        651function vlog(level, loggable, opt_self) {
        652 var logLevel = log.Level.FINE;
        653 if (level > 1) {
        654 logLevel = log.Level.FINEST;
        655 } else if (level > 0) {
        656 logLevel = log.Level.FINER;
        657 }
        658
        659 if (typeof loggable === 'function') {
        660 loggable = loggable.bind(opt_self);
        661 }
        662
        663 log.log(LOG, logLevel, loggable);
        664}
        665
        666
        667/**
        668 * Generates an error to capture the current stack trace.
        669 * @param {string} name Error name for this stack trace.
        670 * @param {string} msg Message to record.
        671 * @param {!Function} topFn The function that should appear at the top of the
        672 * stack; only applicable in V8.
        673 * @return {!Error} The generated error.
        674 */
        675promise.captureStackTrace = function(name, msg, topFn) {
        676 var e = Error(msg);
        677 e.name = name;
        678 if (Error.captureStackTrace) {
        679 Error.captureStackTrace(e, topFn);
        680 } else {
        681 var stack = stacktrace.getStack(e);
        682 e.stack = e.toString();
        683 if (stack) {
        684 e.stack += '\n' + stack;
        685 }
        686 }
        687 return e;
        688};
        689
        690
        691/**
        692 * Error used when the computation of a promise is cancelled.
        693 *
        694 * @unrestricted
        695 */
        696promise.CancellationError = goog.defineClass(DebugError, {
        697 /**
        698 * @param {string=} opt_msg The cancellation message.
        699 */
        700 constructor: function(opt_msg) {
        701 promise.CancellationError.base(this, 'constructor', opt_msg);
        702
        703 /** @override */
        704 this.name = 'CancellationError';
        705
        706 /** @private {boolean} */
        707 this.silent_ = false;
        708 },
        709
        710 statics: {
        711 /**
        712 * Wraps the given error in a CancellationError.
        713 *
        714 * @param {*} error The error to wrap.
        715 * @param {string=} opt_msg The prefix message to use.
        716 * @return {!promise.CancellationError} A cancellation error.
        717 */
        718 wrap: function(error, opt_msg) {
        719 var message;
        720 if (error instanceof promise.CancellationError) {
        721 return new promise.CancellationError(
        722 opt_msg ? (opt_msg + ': ' + error.message) : error.message);
        723 } else if (opt_msg) {
        724 message = opt_msg;
        725 if (error) {
        726 message += ': ' + error;
        727 }
        728 return new promise.CancellationError(message);
        729 }
        730 if (error) {
        731 message = error + '';
        732 }
        733 return new promise.CancellationError(message);
        734 }
        735 }
        736});
        737
        738
        739/**
        740 * Error used to cancel tasks when a control flow is reset.
        741 * @unrestricted
        742 * @final
        743 */
        744var FlowResetError = goog.defineClass(promise.CancellationError, {
        745 constructor: function() {
        746 FlowResetError.base(this, 'constructor', 'ControlFlow was reset');
        747
        748 /** @override */
        749 this.name = 'FlowResetError';
        750
        751 this.silent_ = true;
        752 }
        753});
        754
        755
        756/**
        757 * Error used to cancel tasks that have been discarded due to an uncaught error
        758 * reported earlier in the control flow.
        759 * @unrestricted
        760 * @final
        761 */
        762var DiscardedTaskError = goog.defineClass(promise.CancellationError, {
        763 /** @param {*} error The original error. */
        764 constructor: function(error) {
        765 if (error instanceof DiscardedTaskError) {
        766 return /** @type {!DiscardedTaskError} */(error);
        767 }
        768
        769 var msg = '';
        770 if (error) {
        771 msg = ': ' + (typeof error.message === 'string' ? error.message : error);
        772 }
        773
        774 DiscardedTaskError.base(this, 'constructor',
        775 'Task was discarded due to a previous failure' + msg);
        776
        777 /** @override */
        778 this.name = 'DiscardedTaskError';
        779 this.silent_ = true;
        780 }
        781});
        782
        783
        784/**
        785 * Error used when there are multiple unhandled promise rejections detected
        786 * within a task or callback.
        787 *
        788 * @unrestricted
        789 * @final
        790 */
        791promise.MultipleUnhandledRejectionError = goog.defineClass(DebugError, {
        792 /**
        793 * @param {!(Set<*>)} errors The errors to report.
        794 */
        795 constructor: function(errors) {
        796 promise.MultipleUnhandledRejectionError.base(
        797 this, 'constructor', 'Multiple unhandled promise rejections reported');
        798
        799 /** @override */
        800 this.name = 'MultipleUnhandledRejectionError';
        801
        802 /** @type {!Set<*>} */
        803 this.errors = errors;
        804 }
        805});
        806
        807
        808/**
        809 * Property used to flag constructor's as implementing the Thenable interface
        810 * for runtime type checking.
        811 * @type {string}
        812 * @const
        813 */
        814var IMPLEMENTED_BY_PROP = '$webdriver_Thenable';
        815
        816
        817/**
        818 * Thenable is a promise-like object with a {@code then} method which may be
        819 * used to schedule callbacks on a promised value.
        820 *
        821 * @interface
        822 * @extends {IThenable<T>}
        823 * @template T
        824 */
        825promise.Thenable = goog.defineClass(null, {
        826 statics: {
        827 /**
        828 * Adds a property to a class prototype to allow runtime checks of whether
        829 * instances of that class implement the Thenable interface. This function
        830 * will also ensure the prototype's {@code then} function is exported from
        831 * compiled code.
        832 * @param {function(new: promise.Thenable, ...?)} ctor The
        833 * constructor whose prototype to modify.
        834 */
        835 addImplementation: function(ctor) {
        836 // Based on goog.promise.Thenable.isImplementation.
        837 ctor.prototype['then'] = ctor.prototype.then;
        838 try {
        839 // Old IE7 does not support defineProperty; IE8 only supports it for
        840 // DOM elements.
        841 Object.defineProperty(
        842 ctor.prototype,
        843 IMPLEMENTED_BY_PROP,
        844 {'value': true, 'enumerable': false});
        845 } catch (ex) {
        846 ctor.prototype[IMPLEMENTED_BY_PROP] = true;
        847 }
        848 },
        849
        850 /**
        851 * Checks if an object has been tagged for implementing the Thenable
        852 * interface as defined by
        853 * {@link webdriver.promise.Thenable.addImplementation}.
        854 * @param {*} object The object to test.
        855 * @return {boolean} Whether the object is an implementation of the Thenable
        856 * interface.
        857 */
        858 isImplementation: function(object) {
        859 // Based on goog.promise.Thenable.isImplementation.
        860 if (!object) {
        861 return false;
        862 }
        863 try {
        864 return !!object[IMPLEMENTED_BY_PROP];
        865 } catch (e) {
        866 return false; // Property access seems to be forbidden.
        867 }
        868 }
        869 },
        870
        871 /**
        872 * Cancels the computation of this promise's value, rejecting the promise in
        873 * the process. This method is a no-op if the promise has already been
        874 * resolved.
        875 *
        876 * @param {(string|promise.CancellationError)=} opt_reason The reason this
        877 * promise is being cancelled.
        878 */
        879 cancel: function(opt_reason) {},
        880
        881 /** @return {boolean} Whether this promise's value is still being computed. */
        882 isPending: function() {},
        883
        884 /**
        885 * Registers listeners for when this instance is resolved.
        886 *
        887 * @param {?(function(T): (R|IThenable<R>))=} opt_callback The
        888 * function to call if this promise is successfully resolved. The function
        889 * should expect a single argument: the promise's resolved value.
        890 * @param {?(function(*): (R|IThenable<R>))=} opt_errback
        891 * The function to call if this promise is rejected. The function should
        892 * expect a single argument: the rejection reason.
        893 * @return {!promise.Promise<R>} A new promise which will be
        894 * resolved with the result of the invoked callback.
        895 * @template R
        896 */
        897 then: function(opt_callback, opt_errback) {},
        898
        899 /**
        900 * Registers a listener for when this promise is rejected. This is synonymous
        901 * with the {@code catch} clause in a synchronous API:
        902 *
        903 * // Synchronous API:
        904 * try {
        905 * doSynchronousWork();
        906 * } catch (ex) {
        907 * console.error(ex);
        908 * }
        909 *
        910 * // Asynchronous promise API:
        911 * doAsynchronousWork().thenCatch(function(ex) {
        912 * console.error(ex);
        913 * });
        914 *
        915 * @param {function(*): (R|IThenable<R>)} errback The
        916 * function to call if this promise is rejected. The function should
        917 * expect a single argument: the rejection reason.
        918 * @return {!promise.Promise<R>} A new promise which will be
        919 * resolved with the result of the invoked callback.
        920 * @template R
        921 */
        922 thenCatch: function(errback) {},
        923
        924 /**
        925 * Registers a listener to invoke when this promise is resolved, regardless
        926 * of whether the promise's value was successfully computed. This function
        927 * is synonymous with the {@code finally} clause in a synchronous API:
        928 *
        929 * // Synchronous API:
        930 * try {
        931 * doSynchronousWork();
        932 * } finally {
        933 * cleanUp();
        934 * }
        935 *
        936 * // Asynchronous promise API:
        937 * doAsynchronousWork().thenFinally(cleanUp);
        938 *
        939 * __Note:__ similar to the {@code finally} clause, if the registered
        940 * callback returns a rejected promise or throws an error, it will silently
        941 * replace the rejection error (if any) from this promise:
        942 *
        943 * try {
        944 * throw Error('one');
        945 * } finally {
        946 * throw Error('two'); // Hides Error: one
        947 * }
        948 *
        949 * promise.rejected(Error('one'))
        950 * .thenFinally(function() {
        951 * throw Error('two'); // Hides Error: one
        952 * });
        953 *
        954 * @param {function(): (R|IThenable<R>)} callback The function
        955 * to call when this promise is resolved.
        956 * @return {!promise.Promise<R>} A promise that will be fulfilled
        957 * with the callback result.
        958 * @template R
        959 */
        960 thenFinally: function(callback) {}
        961});
        962
        963
        964
        965/**
        966 * @enum {string}
        967 */
        968var PromiseState = {
        969 PENDING: 'pending',
        970 BLOCKED: 'blocked',
        971 REJECTED: 'rejected',
        972 FULFILLED: 'fulfilled'
        973};
        974
        975
        976/**
        977 * Internal symbol used to store a cancellation handler for
        978 * {@link promise.Promise} objects. This is an internal implementation detail
        979 * used by the {@link TaskQueue} class to monitor for when a promise is
        980 * cancelled without generating an extra promise via then().
        981 */
        982var CANCEL_HANDLER_SYMBOL = Symbol('on cancel');
        983
        984
        985/**
        986 * Represents the eventual value of a completed operation. Each promise may be
        987 * in one of three states: pending, fulfilled, or rejected. Each promise starts
        988 * in the pending state and may make a single transition to either a
        989 * fulfilled or rejected state, at which point the promise is considered
        990 * resolved.
        991 *
        992 * @implements {promise.Thenable<T>}
        993 * @template T
        994 * @see http://promises-aplus.github.io/promises-spec/
        995 * @unrestricted // For using CANCEL_HANDLER_SYMBOL.
        996 */
        997promise.Promise = goog.defineClass(null, {
        998 /**
        999 * @param {function(
        1000 * function((T|IThenable<T>|Thenable)=),
        1001 * function(*=))} resolver
        1002 * Function that is invoked immediately to begin computation of this
        1003 * promise's value. The function should accept a pair of callback
        1004 * functions, one for fulfilling the promise and another for rejecting it.
        1005 * @param {promise.ControlFlow=} opt_flow The control flow
        1006 * this instance was created under. Defaults to the currently active flow.
        1007 */
        1008 constructor: function(resolver, opt_flow) {
        1009 goog.getUid(this);
        1010
        1011 /** @private {!promise.ControlFlow} */
        1012 this.flow_ = opt_flow || promise.controlFlow();
        1013
        1014 /** @private {Error} */
        1015 this.stack_ = null;
        1016 if (promise.LONG_STACK_TRACES) {
        1017 this.stack_ = promise.captureStackTrace(
        1018 'Promise', 'new', promise.Promise);
        1019 }
        1020
        1021 /** @private {promise.Promise<?>} */
        1022 this.parent_ = null;
        1023
        1024 /** @private {Array<!Task>} */
        1025 this.callbacks_ = null;
        1026
        1027 /** @private {PromiseState} */
        1028 this.state_ = PromiseState.PENDING;
        1029
        1030 /** @private {boolean} */
        1031 this.handled_ = false;
        1032
        1033 /** @private {*} */
        1034 this.value_ = undefined;
        1035
        1036 /** @private {TaskQueue} */
        1037 this.queue_ = null;
        1038
        1039 /** @private {(function(promise.CancellationError)|null)} */
        1040 this[CANCEL_HANDLER_SYMBOL] = null;
        1041
        1042 try {
        1043 var self = this;
        1044 resolver(function(value) {
        1045 self.resolve_(PromiseState.FULFILLED, value);
        1046 }, function(reason) {
        1047 self.resolve_(PromiseState.REJECTED, reason);
        1048 });
        1049 } catch (ex) {
        1050 this.resolve_(PromiseState.REJECTED, ex);
        1051 }
        1052 },
        1053
        1054 /** @override */
        1055 toString: function() {
        1056 return 'Promise::' + goog.getUid(this) +
        1057 ' {[[PromiseStatus]]: "' + this.state_ + '"}';
        1058 },
        1059
        1060 /**
        1061 * Resolves this promise. If the new value is itself a promise, this function
        1062 * will wait for it to be resolved before notifying the registered listeners.
        1063 * @param {PromiseState} newState The promise's new state.
        1064 * @param {*} newValue The promise's new value.
        1065 * @throws {TypeError} If {@code newValue === this}.
        1066 * @private
        1067 */
        1068 resolve_: function(newState, newValue) {
        1069 if (PromiseState.PENDING !== this.state_) {
        1070 return;
        1071 }
        1072
        1073 if (newValue === this) {
        1074 // See promise a+, 2.3.1
        1075 // http://promises-aplus.github.io/promises-spec/#point-48
        1076 newValue = new TypeError('A promise may not resolve to itself');
        1077 newState = PromiseState.REJECTED;
        1078 }
        1079
        1080 this.parent_ = null;
        1081 this.state_ = PromiseState.BLOCKED;
        1082
        1083 if (newState !== PromiseState.REJECTED) {
        1084 if (promise.Thenable.isImplementation(newValue)) {
        1085 // 2.3.2
        1086 newValue = /** @type {!promise.Thenable} */(newValue);
        1087 newValue.then(
        1088 this.unblockAndResolve_.bind(this, PromiseState.FULFILLED),
        1089 this.unblockAndResolve_.bind(this, PromiseState.REJECTED));
        1090 return;
        1091
        1092 } else if (goog.isObject(newValue)) {
        1093 // 2.3.3
        1094
        1095 try {
        1096 // 2.3.3.1
        1097 var then = newValue['then'];
        1098 } catch (e) {
        1099 // 2.3.3.2
        1100 this.state_ = PromiseState.REJECTED;
        1101 this.value_ = e;
        1102 this.scheduleNotifications_();
        1103 return;
        1104 }
        1105
        1106 // NB: goog.isFunction is loose and will accept instanceof Function.
        1107 if (typeof then === 'function') {
        1108 // 2.3.3.3
        1109 this.invokeThen_(newValue, then);
        1110 return;
        1111 }
        1112 }
        1113 }
        1114
        1115 if (newState === PromiseState.REJECTED &&
        1116 isError(newValue) && newValue.stack && this.stack_) {
        1117 newValue.stack += '\nFrom: ' + (this.stack_.stack || this.stack_);
        1118 }
        1119
        1120 // 2.3.3.4 and 2.3.4
        1121 this.state_ = newState;
        1122 this.value_ = newValue;
        1123 this.scheduleNotifications_();
        1124 },
        1125
        1126 /**
        1127 * Invokes a thenable's "then" method according to 2.3.3.3 of the promise
        1128 * A+ spec.
        1129 * @param {!Object} x The thenable object.
        1130 * @param {!Function} then The "then" function to invoke.
        1131 * @private
        1132 */
        1133 invokeThen_: function(x, then) {
        1134 var called = false;
        1135 var self = this;
        1136
        1137 var resolvePromise = function(value) {
        1138 if (!called) { // 2.3.3.3.3
        1139 called = true;
        1140 // 2.3.3.3.1
        1141 self.unblockAndResolve_(PromiseState.FULFILLED, value);
        1142 }
        1143 };
        1144
        1145 var rejectPromise = function(reason) {
        1146 if (!called) { // 2.3.3.3.3
        1147 called = true;
        1148 // 2.3.3.3.2
        1149 self.unblockAndResolve_(PromiseState.REJECTED, reason);
        1150 }
        1151 };
        1152
        1153 try {
        1154 // 2.3.3.3
        1155 then.call(x, resolvePromise, rejectPromise);
        1156 } catch (e) {
        1157 // 2.3.3.3.4.2
        1158 rejectPromise(e);
        1159 }
        1160 },
        1161
        1162 /**
        1163 * @param {PromiseState} newState The promise's new state.
        1164 * @param {*} newValue The promise's new value.
        1165 * @private
        1166 */
        1167 unblockAndResolve_: function(newState, newValue) {
        1168 if (this.state_ === PromiseState.BLOCKED) {
        1169 this.state_ = PromiseState.PENDING;
        1170 this.resolve_(newState, newValue);
        1171 }
        1172 },
        1173
        1174 /**
        1175 * @private
        1176 */
        1177 scheduleNotifications_: function() {
        1178 vlog(2, () => this + ' scheduling notifications', this);
        1179
        1180 this[CANCEL_HANDLER_SYMBOL] = null;
        1181 if (this.value_ instanceof promise.CancellationError
        1182 && this.value_.silent_) {
        1183 this.callbacks_ = null;
        1184 }
        1185
        1186 if (!this.queue_) {
        1187 this.queue_ = this.flow_.getActiveQueue_();
        1188 }
        1189
        1190 if (!this.handled_ &&
        1191 this.state_ === PromiseState.REJECTED &&
        1192 !(this.value_ instanceof promise.CancellationError)) {
        1193 this.queue_.addUnhandledRejection(this);
        1194 }
        1195 this.queue_.scheduleCallbacks(this);
        1196 },
        1197
        1198 /** @override */
        1199 cancel: function(opt_reason) {
        1200 if (!canCancel(this)) {
        1201 return;
        1202 }
        1203
        1204 if (this.parent_ && canCancel(this.parent_)) {
        1205 this.parent_.cancel(opt_reason);
        1206 } else {
        1207 var reason = promise.CancellationError.wrap(opt_reason);
        1208 if (this[CANCEL_HANDLER_SYMBOL]) {
        1209 this[CANCEL_HANDLER_SYMBOL](reason);
        1210 this[CANCEL_HANDLER_SYMBOL] = null;
        1211 }
        1212
        1213 if (this.state_ === PromiseState.BLOCKED) {
        1214 this.unblockAndResolve_(PromiseState.REJECTED, reason);
        1215 } else {
        1216 this.resolve_(PromiseState.REJECTED, reason);
        1217 }
        1218 }
        1219
        1220 function canCancel(promise) {
        1221 return promise.state_ === PromiseState.PENDING
        1222 || promise.state_ === PromiseState.BLOCKED;
        1223 }
        1224 },
        1225
        1226 /** @override */
        1227 isPending: function() {
        1228 return this.state_ === PromiseState.PENDING;
        1229 },
        1230
        1231 /** @override */
        1232 then: function(opt_callback, opt_errback) {
        1233 return this.addCallback_(
        1234 opt_callback, opt_errback, 'then', promise.Promise.prototype.then);
        1235 },
        1236
        1237 /** @override */
        1238 thenCatch: function(errback) {
        1239 return this.addCallback_(
        1240 null, errback, 'thenCatch', promise.Promise.prototype.thenCatch);
        1241 },
        1242
        1243 /** @override */
        1244 thenFinally: function(callback) {
        1245 var error;
        1246 var mustThrow = false;
        1247 return this.then(function() {
        1248 return callback();
        1249 }, function(err) {
        1250 error = err;
        1251 mustThrow = true;
        1252 return callback();
        1253 }).then(function() {
        1254 if (mustThrow) {
        1255 throw error;
        1256 }
        1257 });
        1258 },
        1259
        1260 /**
        1261 * Registers a new callback with this promise
        1262 * @param {(function(T): (R|IThenable<R>)|null|undefined)} callback The
        1263 * fulfillment callback.
        1264 * @param {(function(*): (R|IThenable<R>)|null|undefined)} errback The
        1265 * rejection callback.
        1266 * @param {string} name The callback name.
        1267 * @param {!Function} fn The function to use as the top of the stack when
        1268 * recording the callback's creation point.
        1269 * @return {!promise.Promise<R>} A new promise which will be resolved with the
        1270 * esult of the invoked callback.
        1271 * @template R
        1272 * @private
        1273 */
        1274 addCallback_: function(callback, errback, name, fn) {
        1275 if (!goog.isFunction(callback) && !goog.isFunction(errback)) {
        1276 return this;
        1277 }
        1278
        1279 this.handled_ = true;
        1280 if (this.queue_) {
        1281 this.queue_.clearUnhandledRejection(this);
        1282 }
        1283
        1284 var cb = new Task(
        1285 this.flow_,
        1286 this.invokeCallback_.bind(this, callback, errback),
        1287 name,
        1288 promise.LONG_STACK_TRACES ? {name: 'Promise', top: fn} : undefined);
        1289 cb.promise.parent_ = this;
        1290
        1291 if (this.state_ !== PromiseState.PENDING &&
        1292 this.state_ !== PromiseState.BLOCKED) {
        1293 this.flow_.getActiveQueue_().enqueue(cb);
        1294 } else {
        1295 if (!this.callbacks_) {
        1296 this.callbacks_ = [];
        1297 }
        1298 this.callbacks_.push(cb);
        1299 cb.isVolatile = true;
        1300 this.flow_.getActiveQueue_().enqueue(cb);
        1301 }
        1302
        1303 return cb.promise;
        1304 },
        1305
        1306 /**
        1307 * Invokes a callback function attached to this promise.
        1308 * @param {(function(T): (R|IThenable<R>)|null|undefined)} callback The
        1309 * fulfillment callback.
        1310 * @param {(function(*): (R|IThenable<R>)|null|undefined)} errback The
        1311 * rejection callback.
        1312 * @template R
        1313 * @private
        1314 */
        1315 invokeCallback_: function(callback, errback) {
        1316 var callbackFn = callback;
        1317 if (this.state_ === PromiseState.REJECTED) {
        1318 callbackFn = errback;
        1319 }
        1320
        1321 if (goog.isFunction(callbackFn)) {
        1322 if (promise.isGenerator(callbackFn)) {
        1323 return promise.consume(callbackFn, null, this.value_);
        1324 }
        1325 return callbackFn(this.value_);
        1326 } else if (this.state_ === PromiseState.REJECTED) {
        1327 throw this.value_;
        1328 } else {
        1329 return this.value_;
        1330 }
        1331 }
        1332});
        1333promise.Thenable.addImplementation(promise.Promise);
        1334
        1335
        1336/**
        1337 * Represents a value that will be resolved at some point in the future. This
        1338 * class represents the protected "producer" half of a Promise - each Deferred
        1339 * has a {@code promise} property that may be returned to consumers for
        1340 * registering callbacks, reserving the ability to resolve the deferred to the
        1341 * producer.
        1342 *
        1343 * If this Deferred is rejected and there are no listeners registered before
        1344 * the next turn of the event loop, the rejection will be passed to the
        1345 * {@link webdriver.promise.ControlFlow} as an unhandled failure.
        1346 *
        1347 * @implements {promise.Thenable<T>}
        1348 * @template T
        1349 */
        1350promise.Deferred = goog.defineClass(null, {
        1351 /**
        1352 * @param {promise.ControlFlow=} opt_flow The control flow this instance was
        1353 * created under. This should only be provided during unit tests.
        1354 */
        1355 constructor: function(opt_flow) {
        1356 var fulfill, reject;
        1357
        1358 /** @type {!promise.Promise<T>} */
        1359 this.promise = new promise.Promise(function(f, r) {
        1360 fulfill = f;
        1361 reject = r;
        1362 }, opt_flow);
        1363
        1364 var self = this;
        1365 var checkNotSelf = function(value) {
        1366 if (value === self) {
        1367 throw new TypeError('May not resolve a Deferred with itself');
        1368 }
        1369 };
        1370
        1371 /**
        1372 * Resolves this deferred with the given value. It is safe to call this as a
        1373 * normal function (with no bound "this").
        1374 * @param {(T|IThenable<T>|Thenable)=} opt_value The fulfilled value.
        1375 */
        1376 this.fulfill = function(opt_value) {
        1377 checkNotSelf(opt_value);
        1378 fulfill(opt_value);
        1379 };
        1380
        1381 /**
        1382 * Rejects this promise with the given reason. It is safe to call this as a
        1383 * normal function (with no bound "this").
        1384 * @param {*=} opt_reason The rejection reason.
        1385 */
        1386 this.reject = function(opt_reason) {
        1387 checkNotSelf(opt_reason);
        1388 reject(opt_reason);
        1389 };
        1390 },
        1391
        1392 /** @override */
        1393 isPending: function() {
        1394 return this.promise.isPending();
        1395 },
        1396
        1397 /** @override */
        1398 cancel: function(opt_reason) {
        1399 this.promise.cancel(opt_reason);
        1400 },
        1401
        1402 /**
        1403 * @override
        1404 * @deprecated Use {@code then} from the promise property directly.
        1405 */
        1406 then: function(opt_cb, opt_eb) {
        1407 return this.promise.then(opt_cb, opt_eb);
        1408 },
        1409
        1410 /**
        1411 * @override
        1412 * @deprecated Use {@code thenCatch} from the promise property directly.
        1413 */
        1414 thenCatch: function(opt_eb) {
        1415 return this.promise.thenCatch(opt_eb);
        1416 },
        1417
        1418 /**
        1419 * @override
        1420 * @deprecated Use {@code thenFinally} from the promise property directly.
        1421 */
        1422 thenFinally: function(opt_cb) {
        1423 return this.promise.thenFinally(opt_cb);
        1424 }
        1425});
        1426promise.Thenable.addImplementation(promise.Deferred);
        1427
        1428
        1429/**
        1430 * Tests if a value is an Error-like object. This is more than an straight
        1431 * instanceof check since the value may originate from another context.
        1432 * @param {*} value The value to test.
        1433 * @return {boolean} Whether the value is an error.
        1434 */
        1435function isError(value) {
        1436 return value instanceof Error ||
        1437 goog.isObject(value) &&
        1438 (goog.isString(value.message) ||
        1439 // A special test for goog.testing.JsUnitException.
        1440 value.isJsUnitException);
        1441
        1442}
        1443
        1444
        1445/**
        1446 * Determines whether a {@code value} should be treated as a promise.
        1447 * Any object whose "then" property is a function will be considered a promise.
        1448 *
        1449 * @param {*} value The value to test.
        1450 * @return {boolean} Whether the value is a promise.
        1451 */
        1452promise.isPromise = function(value) {
        1453 return !!value && goog.isObject(value) &&
        1454 // Use array notation so the Closure compiler does not obfuscate away our
        1455 // contract. Use typeof rather than goog.isFunction because
        1456 // goog.isFunction accepts instanceof Function, which the promise spec
        1457 // does not.
        1458 typeof value['then'] === 'function';
        1459};
        1460
        1461
        1462/**
        1463 * Creates a promise that will be resolved at a set time in the future.
        1464 * @param {number} ms The amount of time, in milliseconds, to wait before
        1465 * resolving the promise.
        1466 * @return {!promise.Promise} The promise.
        1467 */
        1468promise.delayed = function(ms) {
        1469 var key;
        1470 return new promise.Promise(function(fulfill) {
        1471 key = setTimeout(function() {
        1472 key = null;
        1473 fulfill();
        1474 }, ms);
        1475 }).thenCatch(function(e) {
        1476 clearTimeout(key);
        1477 key = null;
        1478 throw e;
        1479 });
        1480};
        1481
        1482
        1483/**
        1484 * Creates a new deferred object.
        1485 * @return {!promise.Deferred<T>} The new deferred object.
        1486 * @template T
        1487 */
        1488promise.defer = function() {
        1489 return new promise.Deferred();
        1490};
        1491
        1492
        1493/**
        1494 * Creates a promise that has been resolved with the given value.
        1495 * @param {T=} opt_value The resolved value.
        1496 * @return {!promise.Promise<T>} The resolved promise.
        1497 * @template T
        1498 */
        1499promise.fulfilled = function(opt_value) {
        1500 if (opt_value instanceof promise.Promise) {
        1501 return opt_value;
        1502 }
        1503 return new promise.Promise(function(fulfill) {
        1504 fulfill(opt_value);
        1505 });
        1506};
        1507
        1508
        1509/**
        1510 * Creates a promise that has been rejected with the given reason.
        1511 * @param {*=} opt_reason The rejection reason; may be any value, but is
        1512 * usually an Error or a string.
        1513 * @return {!promise.Promise<T>} The rejected promise.
        1514 * @template T
        1515 */
        1516promise.rejected = function(opt_reason) {
        1517 if (opt_reason instanceof promise.Promise) {
        1518 return opt_reason;
        1519 }
        1520 return new promise.Promise(function(_, reject) {
        1521 reject(opt_reason);
        1522 });
        1523};
        1524
        1525
        1526/**
        1527 * Wraps a function that expects a node-style callback as its final
        1528 * argument. This callback expects two arguments: an error value (which will be
        1529 * null if the call succeeded), and the success value as the second argument.
        1530 * The callback will the resolve or reject the returned promise, based on its arguments.
        1531 * @param {!Function} fn The function to wrap.
        1532 * @param {...?} var_args The arguments to apply to the function, excluding the
        1533 * final callback.
        1534 * @return {!promise.Promise} A promise that will be resolved with the
        1535 * result of the provided function's callback.
        1536 */
        1537promise.checkedNodeCall = function(fn, var_args) {
        1538 var args = Arrays.slice(arguments, 1);
        1539 return new promise.Promise(function(fulfill, reject) {
        1540 try {
        1541 args.push(function(error, value) {
        1542 error ? reject(error) : fulfill(value);
        1543 });
        1544 fn.apply(undefined, args);
        1545 } catch (ex) {
        1546 reject(ex);
        1547 }
        1548 });
        1549};
        1550
        1551
        1552/**
        1553 * Registers an observer on a promised {@code value}, returning a new promise
        1554 * that will be resolved when the value is. If {@code value} is not a promise,
        1555 * then the return promise will be immediately resolved.
        1556 * @param {*} value The value to observe.
        1557 * @param {Function=} opt_callback The function to call when the value is
        1558 * resolved successfully.
        1559 * @param {Function=} opt_errback The function to call when the value is
        1560 * rejected.
        1561 * @return {!promise.Promise} A new promise.
        1562 */
        1563promise.when = function(value, opt_callback, opt_errback) {
        1564 if (promise.Thenable.isImplementation(value)) {
        1565 return value.then(opt_callback, opt_errback);
        1566 }
        1567
        1568 return new promise.Promise(function(fulfill, reject) {
        1569 promise.asap(value, fulfill, reject);
        1570 }).then(opt_callback, opt_errback);
        1571};
        1572
        1573
        1574/**
        1575 * Invokes the appropriate callback function as soon as a promised
        1576 * {@code value} is resolved. This function is similar to
        1577 * {@link webdriver.promise.when}, except it does not return a new promise.
        1578 * @param {*} value The value to observe.
        1579 * @param {Function} callback The function to call when the value is
        1580 * resolved successfully.
        1581 * @param {Function=} opt_errback The function to call when the value is
        1582 * rejected.
        1583 */
        1584promise.asap = function(value, callback, opt_errback) {
        1585 if (promise.isPromise(value)) {
        1586 value.then(callback, opt_errback);
        1587
        1588 // Maybe a Dojo-like deferred object?
        1589 } else if (!!value && goog.isObject(value) &&
        1590 goog.isFunction(value.addCallbacks)) {
        1591 value.addCallbacks(callback, opt_errback);
        1592
        1593 // A raw value, return a resolved promise.
        1594 } else if (callback) {
        1595 callback(value);
        1596 }
        1597};
        1598
        1599
        1600/**
        1601 * Given an array of promises, will return a promise that will be fulfilled
        1602 * with the fulfillment values of the input array's values. If any of the
        1603 * input array's promises are rejected, the returned promise will be rejected
        1604 * with the same reason.
        1605 *
        1606 * @param {!Array<(T|!promise.Promise<T>)>} arr An array of
        1607 * promises to wait on.
        1608 * @return {!promise.Promise<!Array<T>>} A promise that is
        1609 * fulfilled with an array containing the fulfilled values of the
        1610 * input array, or rejected with the same reason as the first
        1611 * rejected value.
        1612 * @template T
        1613 */
        1614promise.all = function(arr) {
        1615 return new promise.Promise(function(fulfill, reject) {
        1616 var n = arr.length;
        1617 var values = [];
        1618
        1619 if (!n) {
        1620 fulfill(values);
        1621 return;
        1622 }
        1623
        1624 var toFulfill = n;
        1625 var onFulfilled = function(index, value) {
        1626 values[index] = value;
        1627 toFulfill--;
        1628 if (toFulfill == 0) {
        1629 fulfill(values);
        1630 }
        1631 };
        1632
        1633 for (var i = 0; i < n; ++i) {
        1634 promise.asap(arr[i], goog.partial(onFulfilled, i), reject);
        1635 }
        1636 });
        1637};
        1638
        1639
        1640/**
        1641 * Calls a function for each element in an array and inserts the result into a
        1642 * new array, which is used as the fulfillment value of the promise returned
        1643 * by this function.
        1644 *
        1645 * If the return value of the mapping function is a promise, this function
        1646 * will wait for it to be fulfilled before inserting it into the new array.
        1647 *
        1648 * If the mapping function throws or returns a rejected promise, the
        1649 * promise returned by this function will be rejected with the same reason.
        1650 * Only the first failure will be reported; all subsequent errors will be
        1651 * silently ignored.
        1652 *
        1653 * @param {!(Array<TYPE>|promise.Promise<!Array<TYPE>>)} arr The
        1654 * array to iterator over, or a promise that will resolve to said array.
        1655 * @param {function(this: SELF, TYPE, number, !Array<TYPE>): ?} fn The
        1656 * function to call for each element in the array. This function should
        1657 * expect three arguments (the element, the index, and the array itself.
        1658 * @param {SELF=} opt_self The object to be used as the value of 'this' within
        1659 * {@code fn}.
        1660 * @template TYPE, SELF
        1661 */
        1662promise.map = function(arr, fn, opt_self) {
        1663 return promise.fulfilled(arr).then(function(v) {
        1664 goog.asserts.assertNumber(v.length, 'not an array like value');
        1665 var arr = /** @type {!Array} */(v);
        1666 return new promise.Promise(function(fulfill, reject) {
        1667 var n = arr.length;
        1668 var values = new Array(n);
        1669 (function processNext(i) {
        1670 for (; i < n; i++) {
        1671 if (i in arr) {
        1672 break;
        1673 }
        1674 }
        1675 if (i >= n) {
        1676 fulfill(values);
        1677 return;
        1678 }
        1679 try {
        1680 promise.asap(
        1681 fn.call(opt_self, arr[i], i, /** @type {!Array} */(arr)),
        1682 function(value) {
        1683 values[i] = value;
        1684 processNext(i + 1);
        1685 },
        1686 reject);
        1687 } catch (ex) {
        1688 reject(ex);
        1689 }
        1690 })(0);
        1691 });
        1692 });
        1693};
        1694
        1695
        1696/**
        1697 * Calls a function for each element in an array, and if the function returns
        1698 * true adds the element to a new array.
        1699 *
        1700 * If the return value of the filter function is a promise, this function
        1701 * will wait for it to be fulfilled before determining whether to insert the
        1702 * element into the new array.
        1703 *
        1704 * If the filter function throws or returns a rejected promise, the promise
        1705 * returned by this function will be rejected with the same reason. Only the
        1706 * first failure will be reported; all subsequent errors will be silently
        1707 * ignored.
        1708 *
        1709 * @param {!(Array<TYPE>|promise.Promise<!Array<TYPE>>)} arr The
        1710 * array to iterator over, or a promise that will resolve to said array.
        1711 * @param {function(this: SELF, TYPE, number, !Array<TYPE>): (
        1712 * boolean|promise.Promise<boolean>)} fn The function
        1713 * to call for each element in the array.
        1714 * @param {SELF=} opt_self The object to be used as the value of 'this' within
        1715 * {@code fn}.
        1716 * @template TYPE, SELF
        1717 */
        1718promise.filter = function(arr, fn, opt_self) {
        1719 return promise.fulfilled(arr).then(function(v) {
        1720 goog.asserts.assertNumber(v.length, 'not an array like value');
        1721 var arr = /** @type {!Array} */(v);
        1722 return new promise.Promise(function(fulfill, reject) {
        1723 var n = arr.length;
        1724 var values = [];
        1725 var valuesLength = 0;
        1726 (function processNext(i) {
        1727 for (; i < n; i++) {
        1728 if (i in arr) {
        1729 break;
        1730 }
        1731 }
        1732 if (i >= n) {
        1733 fulfill(values);
        1734 return;
        1735 }
        1736 try {
        1737 var value = arr[i];
        1738 var include = fn.call(opt_self, value, i, /** @type {!Array} */(arr));
        1739 promise.asap(include, function(include) {
        1740 if (include) {
        1741 values[valuesLength++] = value;
        1742 }
        1743 processNext(i + 1);
        1744 }, reject);
        1745 } catch (ex) {
        1746 reject(ex);
        1747 }
        1748 })(0);
        1749 });
        1750 });
        1751};
        1752
        1753
        1754/**
        1755 * Returns a promise that will be resolved with the input value in a
        1756 * fully-resolved state. If the value is an array, each element will be fully
        1757 * resolved. Likewise, if the value is an object, all keys will be fully
        1758 * resolved. In both cases, all nested arrays and objects will also be
        1759 * fully resolved. All fields are resolved in place; the returned promise will
        1760 * resolve on {@code value} and not a copy.
        1761 *
        1762 * Warning: This function makes no checks against objects that contain
        1763 * cyclical references:
        1764 *
        1765 * var value = {};
        1766 * value['self'] = value;
        1767 * promise.fullyResolved(value); // Stack overflow.
        1768 *
        1769 * @param {*} value The value to fully resolve.
        1770 * @return {!promise.Promise} A promise for a fully resolved version
        1771 * of the input value.
        1772 */
        1773promise.fullyResolved = function(value) {
        1774 if (promise.isPromise(value)) {
        1775 return promise.when(value, fullyResolveValue);
        1776 }
        1777 return fullyResolveValue(value);
        1778};
        1779
        1780
        1781/**
        1782 * @param {*} value The value to fully resolve. If a promise, assumed to
        1783 * already be resolved.
        1784 * @return {!promise.Promise} A promise for a fully resolved version
        1785 * of the input value.
        1786 */
        1787 function fullyResolveValue(value) {
        1788 switch (goog.typeOf(value)) {
        1789 case 'array':
        1790 return fullyResolveKeys(/** @type {!Array} */ (value));
        1791
        1792 case 'object':
        1793 if (promise.isPromise(value)) {
        1794 // We get here when the original input value is a promise that
        1795 // resolves to itself. When the user provides us with such a promise,
        1796 // trust that it counts as a "fully resolved" value and return it.
        1797 // Of course, since it's already a promise, we can just return it
        1798 // to the user instead of wrapping it in another promise.
        1799 return /** @type {!promise.Promise} */ (value);
        1800 }
        1801
        1802 if (goog.isNumber(value.nodeType) &&
        1803 goog.isObject(value.ownerDocument) &&
        1804 goog.isNumber(value.ownerDocument.nodeType)) {
        1805 // DOM node; return early to avoid infinite recursion. Should we
        1806 // only support objects with a certain level of nesting?
        1807 return promise.fulfilled(value);
        1808 }
        1809
        1810 return fullyResolveKeys(/** @type {!Object} */ (value));
        1811
        1812 default: // boolean, function, null, number, string, undefined
        1813 return promise.fulfilled(value);
        1814 }
        1815}
        1816
        1817
        1818/**
        1819 * @param {!(Array|Object)} obj the object to resolve.
        1820 * @return {!promise.Promise} A promise that will be resolved with the
        1821 * input object once all of its values have been fully resolved.
        1822 */
        1823 function fullyResolveKeys(obj) {
        1824 var isArray = goog.isArray(obj);
        1825 var numKeys = isArray ? obj.length : Objects.getCount(obj);
        1826 if (!numKeys) {
        1827 return promise.fulfilled(obj);
        1828 }
        1829
        1830 var numResolved = 0;
        1831 return new promise.Promise(function(fulfill, reject) {
        1832 // In pre-IE9, goog.array.forEach will not iterate properly over arrays
        1833 // containing undefined values because "index in array" returns false
        1834 // when array[index] === undefined (even for x = [undefined, 1]). To get
        1835 // around this, we need to use our own forEach implementation.
        1836 // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be
        1837 // reproduced in IE9 by changing the browser/document modes, it requires an
        1838 // actual pre-IE9 browser. Yay, IE!
        1839 var forEachKey = !isArray ? Objects.forEach : function(arr, fn) {
        1840 var n = arr.length;
        1841 for (var i = 0; i < n; ++i) {
        1842 fn.call(null, arr[i], i, arr);
        1843 }
        1844 };
        1845
        1846 forEachKey(obj, function(partialValue, key) {
        1847 var type = goog.typeOf(partialValue);
        1848 if (type != 'array' && type != 'object') {
        1849 maybeResolveValue();
        1850 return;
        1851 }
        1852
        1853 promise.fullyResolved(partialValue).then(
        1854 function(resolvedValue) {
        1855 obj[key] = resolvedValue;
        1856 maybeResolveValue();
        1857 },
        1858 reject);
        1859 });
        1860
        1861 function maybeResolveValue() {
        1862 if (++numResolved == numKeys) {
        1863 fulfill(obj);
        1864 }
        1865 }
        1866 });
        1867}
        1868
        1869
        1870//////////////////////////////////////////////////////////////////////////////
        1871//
        1872// promise.ControlFlow
        1873//
        1874//////////////////////////////////////////////////////////////////////////////
        1875
        1876
        1877
        1878/**
        1879 * Handles the execution of scheduled tasks, each of which may be an
        1880 * asynchronous operation. The control flow will ensure tasks are executed in
        1881 * the ordered scheduled, starting each task only once those before it have
        1882 * completed.
        1883 *
        1884 * Each task scheduled within this flow may return a
        1885 * {@link webdriver.promise.Promise} to indicate it is an asynchronous
        1886 * operation. The ControlFlow will wait for such promises to be resolved before
        1887 * marking the task as completed.
        1888 *
        1889 * Tasks and each callback registered on a {@link webdriver.promise.Promise}
        1890 * will be run in their own ControlFlow frame. Any tasks scheduled within a
        1891 * frame will take priority over previously scheduled tasks. Furthermore, if any
        1892 * of the tasks in the frame fail, the remainder of the tasks in that frame will
        1893 * be discarded and the failure will be propagated to the user through the
        1894 * callback/task's promised result.
        1895 *
        1896 * Each time a ControlFlow empties its task queue, it will fire an
        1897 * {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event. Conversely,
        1898 * whenever the flow terminates due to an unhandled error, it will remove all
        1899 * remaining tasks in its queue and fire an
        1900 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION
        1901 * UNCAUGHT_EXCEPTION} event. If there are no listeners registered with the
        1902 * flow, the error will be rethrown to the global error handler.
        1903 *
        1904 * Refer to the {@link webdriver.promise} module documentation for a detailed
        1905 * explanation of how the ControlFlow coordinates task execution.
        1906 *
        1907 * @final
        1908 */
        1909promise.ControlFlow = goog.defineClass(EventEmitter, {
        1910 // TODO: remove this empty comment when the compiler properly handles
        1911 // goog.defineClass with a missing constructor comment.
        1912 /** @constructor */
        1913 constructor: function() {
        1914 promise.ControlFlow.base(this, 'constructor');
        1915
        1916 /** @private {boolean} */
        1917 this.propagateUnhandledRejections_ = true;
        1918
        1919 /** @private {TaskQueue} */
        1920 this.activeQueue_ = null;
        1921
        1922 /** @private {Set<TaskQueue>} */
        1923 this.taskQueues_ = null;
        1924
        1925 /**
        1926 * Micro task that controls shutting down the control flow. Upon shut down,
        1927 * the flow will emit an
        1928 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events
        1929 * always follow a brief timeout in order to catch latent errors from the
        1930 * last completed task. If this task had a callback registered, but no
        1931 * errback, and the task fails, the unhandled failure would not be reported
        1932 * by the promise system until the next turn of the event loop:
        1933 *
        1934 * // Schedule 1 task that fails.
        1935 * var result = promise.controlFlow().schedule('example',
        1936 * function() { return promise.rejected('failed'); });
        1937 * // Set a callback on the result. This delays reporting the unhandled
        1938 * // failure for 1 turn of the event loop.
        1939 * result.then(goog.nullFunction);
        1940 *
        1941 * @private {MicroTask}
        1942 */
        1943 this.shutdownTask_ = null;
        1944
        1945 /**
        1946 * ID for a long running interval used to keep a Node.js process running
        1947 * while a control flow's event loop is still working. This is a cheap hack
        1948 * required since JS events are only scheduled to run when there is
        1949 * _actually_ something to run. When a control flow is waiting on a task,
        1950 * there will be nothing in the JS event loop and the process would
        1951 * terminate without this.
        1952 *
        1953 * @private {?number}
        1954 */
        1955 this.hold_ = null;
        1956 },
        1957
        1958 /**
        1959 * Returns a string representation of this control flow, which is its current
        1960 * {@link #getSchedule() schedule}, sans task stack traces.
        1961 * @return {string} The string representation of this contorl flow.
        1962 * @override
        1963 */
        1964 toString: function() {
        1965 return this.getSchedule();
        1966 },
        1967
        1968 /**
        1969 * Sets whether any unhandled rejections should propagate up through the
        1970 * control flow stack and cause rejections within parent tasks. If error
        1971 * propagation is disabled, tasks will not be aborted when an unhandled
        1972 * promise rejection is detected, but the rejection _will_ trigger an
        1973 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION}
        1974 * event.
        1975 *
        1976 * The default behavior is to propagate all unhandled rejections. _The use
        1977 * of this option is highly discouraged._
        1978 *
        1979 * @param {boolean} propagate whether to propagate errors.
        1980 */
        1981 setPropagateUnhandledRejections: function(propagate) {
        1982 this.propagateUnhandledRejections_ = propagate;
        1983 },
        1984
        1985 /**
        1986 * @return {boolean} Whether this flow is currently idle.
        1987 */
        1988 isIdle: function() {
        1989 return !this.shutdownTask_ && (!this.taskQueues_ || !this.taskQueues_.size);
        1990 },
        1991
        1992 /**
        1993 * Resets this instance, clearing its queue and removing all event listeners.
        1994 */
        1995 reset: function() {
        1996 this.cancelQueues_(new FlowResetError);
        1997 this.emit(promise.ControlFlow.EventType.RESET);
        1998 this.removeAllListeners();
        1999 this.cancelShutdown_();
        2000 },
        2001
        2002 /**
        2003 * Generates an annotated string describing the internal state of this control
        2004 * flow, including the currently executing as well as pending tasks. If
        2005 * {@code opt_includeStackTraces === true}, the string will include the
        2006 * stack trace from when each task was scheduled.
        2007 * @param {string=} opt_includeStackTraces Whether to include the stack traces
        2008 * from when each task was scheduled. Defaults to false.
        2009 * @return {string} String representation of this flow's internal state.
        2010 */
        2011 getSchedule: function(opt_includeStackTraces) {
        2012 var ret = 'ControlFlow::' + goog.getUid(this);
        2013 var activeQueue = this.activeQueue_;
        2014 if (!this.taskQueues_ || !this.taskQueues_.size) {
        2015 return ret;
        2016 }
        2017 var childIndent = '| ';
        2018 for (var q of this.taskQueues_) {
        2019 ret += '\n' + printQ(q, childIndent);
        2020 }
        2021 return ret;
        2022
        2023 function printQ(q, indent) {
        2024 var ret = q.toString();
        2025 if (q === activeQueue) {
        2026 ret = '(active) ' + ret;
        2027 }
        2028 var prefix = indent + childIndent;
        2029 if (q.pending_) {
        2030 if (q.pending_.q.state_ !== TaskQueueState.FINISHED) {
        2031 ret += '\n' + prefix + '(pending) ' + q.pending_.task;
        2032 ret += '\n' + printQ(q.pending_.q, prefix + childIndent);
        2033 } else {
        2034 ret += '\n' + prefix + '(blocked) ' + q.pending_.task;
        2035 }
        2036 }
        2037 if (q.interrupts_) {
        2038 q.interrupts_.forEach((task) => {
        2039 ret += '\n' + prefix + task;
        2040 });
        2041 }
        2042 if (q.tasks_) {
        2043 q.tasks_.forEach((task) => ret += printTask(task, '\n' + prefix));
        2044 }
        2045 return indent + ret;
        2046 }
        2047
        2048 function printTask(task, prefix) {
        2049 var ret = prefix + task;
        2050 if (opt_includeStackTraces && task.promise.stack_) {
        2051 ret += prefix + childIndent
        2052 + (task.promise.stack_.stack || task.promise.stack_)
        2053 .replace(/\n/g, prefix);
        2054 }
        2055 return ret;
        2056 }
        2057 },
        2058
        2059 /**
        2060 * Returns the currently actively task queue for this flow. If there is no
        2061 * active queue, one will be created.
        2062 * @return {!TaskQueue} the currently active task queue for this flow.
        2063 * @private
        2064 */
        2065 getActiveQueue_: function() {
        2066 if (this.activeQueue_) {
        2067 return this.activeQueue_;
        2068 }
        2069
        2070 this.activeQueue_ = new TaskQueue(this);
        2071 if (!this.taskQueues_) {
        2072 this.taskQueues_ = new Set();
        2073 }
        2074 this.taskQueues_.add(this.activeQueue_);
        2075 this.activeQueue_
        2076 .once('end', this.onQueueEnd_, this)
        2077 .once('error', this.onQueueError_, this);
        2078
        2079 asyncRun(() => this.activeQueue_ = null, this);
        2080 this.activeQueue_.start();
        2081 return this.activeQueue_;
        2082 },
        2083
        2084 /**
        2085 * Schedules a task for execution. If there is nothing currently in the
        2086 * queue, the task will be executed in the next turn of the event loop. If
        2087 * the task function is a generator, the task will be executed using
        2088 * {@link webdriver.promise.consume}.
        2089 *
        2090 * @param {function(): (T|promise.Promise<T>)} fn The function to
        2091 * call to start the task. If the function returns a
        2092 * {@link webdriver.promise.Promise}, this instance will wait for it to be
        2093 * resolved before starting the next task.
        2094 * @param {string=} opt_description A description of the task.
        2095 * @return {!promise.Promise<T>} A promise that will be resolved
        2096 * with the result of the action.
        2097 * @template T
        2098 */
        2099 execute: function(fn, opt_description) {
        2100 if (promise.isGenerator(fn)) {
        2101 fn = goog.partial(promise.consume, fn);
        2102 }
        2103
        2104 if (!this.hold_) {
        2105 var holdIntervalMs = 2147483647; // 2^31-1; max timer length for Node.js
        2106 this.hold_ = setInterval(goog.nullFunction, holdIntervalMs);
        2107 }
        2108
        2109 var task = new Task(
        2110 this, fn, opt_description || '<anonymous>',
        2111 {name: 'Task', top: promise.ControlFlow.prototype.execute});
        2112
        2113 var q = this.getActiveQueue_();
        2114 q.enqueue(task);
        2115 this.emit(promise.ControlFlow.EventType.SCHEDULE_TASK, task.description);
        2116 return task.promise;
        2117 },
        2118
        2119 /**
        2120 * Inserts a {@code setTimeout} into the command queue. This is equivalent to
        2121 * a thread sleep in a synchronous programming language.
        2122 *
        2123 * @param {number} ms The timeout delay, in milliseconds.
        2124 * @param {string=} opt_description A description to accompany the timeout.
        2125 * @return {!promise.Promise} A promise that will be resolved with
        2126 * the result of the action.
        2127 */
        2128 timeout: function(ms, opt_description) {
        2129 return this.execute(function() {
        2130 return promise.delayed(ms);
        2131 }, opt_description);
        2132 },
        2133
        2134 /**
        2135 * Schedules a task that shall wait for a condition to hold. Each condition
        2136 * function may return any value, but it will always be evaluated as a
        2137 * boolean.
        2138 *
        2139 * Condition functions may schedule sub-tasks with this instance, however,
        2140 * their execution time will be factored into whether a wait has timed out.
        2141 *
        2142 * In the event a condition returns a Promise, the polling loop will wait for
        2143 * it to be resolved before evaluating whether the condition has been
        2144 * satisfied. The resolution time for a promise is factored into whether a
        2145 * wait has timed out.
        2146 *
        2147 * If the condition function throws, or returns a rejected promise, the
        2148 * wait task will fail.
        2149 *
        2150 * If the condition is defined as a promise, the flow will wait for it to
        2151 * settle. If the timeout expires before the promise settles, the promise
        2152 * returned by this function will be rejected.
        2153 *
        2154 * If this function is invoked with `timeout === 0`, or the timeout is
        2155 * omitted, the flow will wait indefinitely for the condition to be satisfied.
        2156 *
        2157 * @param {(!promise.Promise<T>|function())} condition The condition to poll,
        2158 * or a promise to wait on.
        2159 * @param {number=} opt_timeout How long to wait, in milliseconds, for the
        2160 * condition to hold before timing out. If omitted, the flow will wait
        2161 * indefinitely.
        2162 * @param {string=} opt_message An optional error message to include if the
        2163 * wait times out; defaults to the empty string.
        2164 * @return {!promise.Promise<T>} A promise that will be fulfilled
        2165 * when the condition has been satisified. The promise shall be rejected
        2166 * if the wait times out waiting for the condition.
        2167 * @throws {TypeError} If condition is not a function or promise or if timeout
        2168 * is not a number >= 0.
        2169 * @template T
        2170 */
        2171 wait: function(condition, opt_timeout, opt_message) {
        2172 var timeout = opt_timeout || 0;
        2173 if (!goog.isNumber(timeout) || timeout < 0) {
        2174 throw TypeError('timeout must be a number >= 0: ' + timeout);
        2175 }
        2176
        2177 if (promise.isPromise(condition)) {
        2178 return this.execute(function() {
        2179 if (!timeout) {
        2180 return condition;
        2181 }
        2182 return new promise.Promise(function(fulfill, reject) {
        2183 var start = goog.now();
        2184 var timer = setTimeout(function() {
        2185 timer = null;
        2186 reject(Error((opt_message ? opt_message + '\n' : '') +
        2187 'Timed out waiting for promise to resolve after ' +
        2188 (goog.now() - start) + 'ms'));
        2189 }, timeout);
        2190
        2191 /** @type {Thenable} */(condition).then(
        2192 function(value) {
        2193 timer && clearTimeout(timer);
        2194 fulfill(value);
        2195 },
        2196 function(error) {
        2197 timer && clearTimeout(timer);
        2198 reject(error);
        2199 });
        2200 });
        2201 }, opt_message || '<anonymous wait: promise resolution>');
        2202 }
        2203
        2204 if (!goog.isFunction(condition)) {
        2205 throw TypeError('Invalid condition; must be a function or promise: ' +
        2206 goog.typeOf(condition));
        2207 }
        2208
        2209 if (promise.isGenerator(condition)) {
        2210 condition = goog.partial(promise.consume, condition);
        2211 }
        2212
        2213 var self = this;
        2214 return this.execute(function() {
        2215 var startTime = goog.now();
        2216 return new promise.Promise(function(fulfill, reject) {
        2217 pollCondition();
        2218
        2219 function pollCondition() {
        2220 var conditionFn = /** @type {function()} */(condition);
        2221 self.execute(conditionFn).then(function(value) {
        2222 var elapsed = goog.now() - startTime;
        2223 if (!!value) {
        2224 fulfill(value);
        2225 } else if (timeout && elapsed >= timeout) {
        2226 reject(new Error((opt_message ? opt_message + '\n' : '') +
        2227 'Wait timed out after ' + elapsed + 'ms'));
        2228 } else {
        2229 // Do not use asyncRun here because we need a non-micro yield
        2230 // here so the UI thread is given a chance when running in a
        2231 // browser.
        2232 setTimeout(pollCondition, 0);
        2233 }
        2234 }, reject);
        2235 }
        2236 });
        2237 }, opt_message || '<anonymous wait>');
        2238 },
        2239
        2240 /**
        2241 * Executes a function in the next available turn of the JavaScript event
        2242 * loop. This ensures the function runs with its own task queue and any
        2243 * scheduled tasks will run in "parallel" to those scheduled in the current
        2244 * function.
        2245 *
        2246 * flow.execute(() => console.log('a'));
        2247 * flow.execute(() => console.log('b'));
        2248 * flow.execute(() => console.log('c'));
        2249 * flow.async(() => {
        2250 * flow.execute(() => console.log('d'));
        2251 * flow.execute(() => console.log('e'));
        2252 * });
        2253 * flow.async(() => {
        2254 * flow.execute(() => console.log('f'));
        2255 * flow.execute(() => console.log('g'));
        2256 * });
        2257 * flow.once('idle', () => console.log('fin'));
        2258 * // a
        2259 * // d
        2260 * // f
        2261 * // b
        2262 * // e
        2263 * // g
        2264 * // c
        2265 * // fin
        2266 *
        2267 * If the function itself throws, the error will be treated the same as an
        2268 * unhandled rejection within the control flow.
        2269 *
        2270 * __NOTE__: This function is considered _unstable_.
        2271 *
        2272 * @param {!Function} fn The function to execute.
        2273 * @param {Object=} opt_self The object in whose context to run the function.
        2274 * @param {...*} var_args Any arguments to pass to the function.
        2275 */
        2276 async: function(fn, opt_self, var_args) {
        2277 asyncRun(function() {
        2278 // Clear any lingering queues, forces getActiveQueue_ to create a new one.
        2279 this.activeQueue_ = null;
        2280 var q = this.getActiveQueue_();
        2281 try {
        2282 q.execute_(fn.bind(opt_self, var_args));
        2283 } catch (ex) {
        2284 var cancellationError = promise.CancellationError.wrap(ex,
        2285 'Function passed to ControlFlow.async() threw');
        2286 cancellationError.silent_ = true;
        2287 q.abort_(cancellationError);
        2288 } finally {
        2289 this.activeQueue_ = null;
        2290 }
        2291 }, this);
        2292 },
        2293
        2294 /**
        2295 * Event handler for when a task queue is exhausted. This starts the shutdown
        2296 * sequence for this instance if there are no remaining task queues: after
        2297 * one turn of the event loop, this object will emit the
        2298 * {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event to signal
        2299 * listeners that it has completed. During this wait, if another task is
        2300 * scheduled, the shutdown will be aborted.
        2301 *
        2302 * @param {!TaskQueue} q the completed task queue.
        2303 * @private
        2304 */
        2305 onQueueEnd_: function(q) {
        2306 if (!this.taskQueues_) {
        2307 return;
        2308 }
        2309 this.taskQueues_.delete(q);
        2310
        2311 vlog(1, () => q + ' has finished');
        2312 vlog(1, () => this.taskQueues_.size + ' queues remain\n' + this, this);
        2313
        2314 if (!this.taskQueues_.size) {
        2315 asserts.assert(!this.shutdownTask_, 'Already have a shutdown task??');
        2316 this.shutdownTask_ = new MicroTask(this.shutdown_, this);
        2317 }
        2318 },
        2319
        2320 /**
        2321 * Event handler for when a task queue terminates with an error. This triggers
        2322 * the cancellation of all other task queues and a
        2323 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event.
        2324 * If there are no error event listeners registered with this instance, the
        2325 * error will be rethrown to the global error handler.
        2326 *
        2327 * @param {*} error the error that caused the task queue to terminate.
        2328 * @param {!TaskQueue} q the task queue.
        2329 * @private
        2330 */
        2331 onQueueError_: function(error, q) {
        2332 if (this.taskQueues_) {
        2333 this.taskQueues_.delete(q);
        2334 }
        2335 this.cancelQueues_(promise.CancellationError.wrap(
        2336 error, 'There was an uncaught error in the control flow'));
        2337 this.cancelShutdown_();
        2338 this.cancelHold_();
        2339
        2340 var listeners = this.listeners(
        2341 promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION);
        2342 if (!listeners.length) {
        2343 throwException(error);
        2344 } else {
        2345 this.reportUncaughtException_(error);
        2346 }
        2347 },
        2348
        2349 /**
        2350 * Cancels all remaining task queues.
        2351 * @param {!promise.CancellationError} reason The cancellation reason.
        2352 * @private
        2353 */
        2354 cancelQueues_: function(reason) {
        2355 reason.silent_ = true;
        2356 if (this.taskQueues_) {
        2357 for (var q of this.taskQueues_) {
        2358 q.removeAllListeners();
        2359 q.abort_(reason);
        2360 }
        2361 this.taskQueues_.clear();
        2362 this.taskQueues_ = null;
        2363 }
        2364 },
        2365
        2366 /**
        2367 * Reports an uncaught exception using a
        2368 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event.
        2369 *
        2370 * @param {*} e the error to report.
        2371 * @private
        2372 */
        2373 reportUncaughtException_: function(e) {
        2374 this.emit(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, e);
        2375 },
        2376
        2377 /** @private */
        2378 cancelHold_: function() {
        2379 if (this.hold_) {
        2380 clearInterval(this.hold_);
        2381 this.hold_ = null;
        2382 }
        2383 },
        2384
        2385 /** @private */
        2386 shutdown_: function() {
        2387 vlog(1, () => 'Going idle: ' + this);
        2388 this.cancelHold_();
        2389 this.shutdownTask_ = null;
        2390 this.emit(promise.ControlFlow.EventType.IDLE);
        2391 },
        2392
        2393 /**
        2394 * Cancels the shutdown sequence if it is currently scheduled.
        2395 * @private
        2396 */
        2397 cancelShutdown_: function() {
        2398 if (this.shutdownTask_) {
        2399 this.shutdownTask_.cancel();
        2400 this.shutdownTask_ = null;
        2401 }
        2402 }
        2403});
        2404
        2405
        2406/**
        2407 * Events that may be emitted by an {@link webdriver.promise.ControlFlow}.
        2408 * @enum {string}
        2409 */
        2410promise.ControlFlow.EventType = {
        2411
        2412 /** Emitted when all tasks have been successfully executed. */
        2413 IDLE: 'idle',
        2414
        2415 /** Emitted when a ControlFlow has been reset. */
        2416 RESET: 'reset',
        2417
        2418 /** Emitted whenever a new task has been scheduled. */
        2419 SCHEDULE_TASK: 'scheduleTask',
        2420
        2421 /**
        2422 * Emitted whenever a control flow aborts due to an unhandled promise
        2423 * rejection. This event will be emitted along with the offending rejection
        2424 * reason. Upon emitting this event, the control flow will empty its task
        2425 * queue and revert to its initial state.
        2426 */
        2427 UNCAUGHT_EXCEPTION: 'uncaughtException'
        2428};
        2429
        2430
        2431/**
        2432 * Wraps a function to execute as a cancellable micro task.
        2433 * @final
        2434 */
        2435var MicroTask = goog.defineClass(null, {
        2436 /**
        2437 * @param {function(this: THIS)} fn The function to run as a micro task.
        2438 * @param {THIS=} opt_scope The scope to run the function in.
        2439 * @template THIS
        2440 */
        2441 constructor: function(fn, opt_scope) {
        2442 /** @private {boolean} */
        2443 this.cancelled_ = false;
        2444 asyncRun(function() {
        2445 if (!this.cancelled_) {
        2446 fn.call(opt_scope);
        2447 }
        2448 }, this);
        2449 },
        2450
        2451 /**
        2452 * Cancels the execution of this task. Note: this will not prevent the task
        2453 * timer from firing, just the invocation of the wrapped function.
        2454 */
        2455 cancel: function() {
        2456 this.cancelled_ = true;
        2457 }
        2458});
        2459
        2460
        2461/**
        2462 * A task to be executed by a {@link webdriver.promise.ControlFlow}.
        2463 *
        2464 * @final
        2465 */
        2466var Task = goog.defineClass(promise.Deferred, {
        2467 /**
        2468 * @param {!promise.ControlFlow} flow The flow this instances belongs
        2469 * to.
        2470 * @param {function(): (T|!promise.Promise<T>)} fn The function to
        2471 * call when the task executes. If it returns a
        2472 * {@link webdriver.promise.Promise}, the flow will wait for it to be
        2473 * resolved before starting the next task.
        2474 * @param {string} description A description of the task for debugging.
        2475 * @param {{name: string, top: !Function}=} opt_stackOptions Options to use
        2476 * when capturing the stacktrace for when this task was created.
        2477 * @constructor
        2478 * @extends {promise.Deferred<T>}
        2479 * @template T
        2480 */
        2481 constructor: function(flow, fn, description, opt_stackOptions) {
        2482 Task.base(this, 'constructor', flow);
        2483
        2484 /** @type {function(): (T|!promise.Promise<T>)} */
        2485 this.execute = fn;
        2486
        2487 /** @type {string} */
        2488 this.description = description;
        2489
        2490 /** @type {TaskQueue} */
        2491 this.queue = null;
        2492
        2493 /**
        2494 * Whether this task is volatile. Volatile tasks may be registered in a
        2495 * a task queue, but will be dropped on the next turn of the JS event loop
        2496 * if still marked volatile.
        2497 * @type {boolean}
        2498 */
        2499 this.isVolatile = false;
        2500
        2501 if (opt_stackOptions) {
        2502 this.promise.stack_ = promise.captureStackTrace(
        2503 opt_stackOptions.name, this.description, opt_stackOptions.top);
        2504 }
        2505 },
        2506
        2507 /** @override */
        2508 toString: function() {
        2509 return 'Task::' + goog.getUid(this) + '<' + this.description + '>';
        2510 }
        2511});
        2512
        2513
        2514/** @enum {string} */
        2515var TaskQueueState = {
        2516 NEW: 'new',
        2517 STARTED: 'started',
        2518 FINISHED: 'finished'
        2519};
        2520
        2521
        2522/**
        2523 * @final
        2524 */
        2525var TaskQueue = goog.defineClass(EventEmitter, {
        2526 /** @param {!promise.ControlFlow} flow . */
        2527 constructor: function(flow) {
        2528 TaskQueue.base(this, 'constructor');
        2529 goog.getUid(this);
        2530
        2531 /** @private {string} */
        2532 this.name_ = 'TaskQueue::' + goog.getUid(this);
        2533
        2534 /** @private {!promise.ControlFlow} */
        2535 this.flow_ = flow;
        2536
        2537 /** @private {!Array<!Task>} */
        2538 this.tasks_ = [];
        2539
        2540 /** @private {Array<!Task>} */
        2541 this.volatileTasks_ = null;
        2542
        2543 /** @private {Array<!Task>} */
        2544 this.interrupts_ = null;
        2545
        2546 /** @private {({task: !Task, q: !TaskQueue}|null)} */
        2547 this.pending_ = null;
        2548
        2549 /** @private {TaskQueueState} */
        2550 this.state_ = TaskQueueState.NEW;
        2551
        2552 /** @private {!Set<!webdriver.promise.Promise>} */
        2553 this.unhandledRejections_ = new Set();
        2554 },
        2555
        2556 /** @override */
        2557 toString: function() {
        2558 return 'TaskQueue::' + goog.getUid(this);
        2559 },
        2560
        2561 /**
        2562 * @param {!webdriver.promise.Promise} promise .
        2563 */
        2564 addUnhandledRejection: function(promise) {
        2565 // TODO: node 4.0.0+
        2566 vlog(2, () => this + ' registering unhandled rejection: ' + promise, this);
        2567 this.unhandledRejections_.add(promise);
        2568 },
        2569
        2570 /**
        2571 * @param {!webdriver.promise.Promise} promise .
        2572 */
        2573 clearUnhandledRejection: function(promise) {
        2574 var deleted = this.unhandledRejections_.delete(promise);
        2575 if (deleted) {
        2576 // TODO: node 4.0.0+
        2577 vlog(2, () => this + ' clearing unhandled rejection: ' + promise, this);
        2578 }
        2579 },
        2580
        2581 /**
        2582 * Enqueues a new task for execution.
        2583 * @param {!Task} task The task to enqueue.
        2584 * @throws {Error} If this instance has already started execution.
        2585 */
        2586 enqueue: function(task) {
        2587 if (this.state_ !== TaskQueueState.NEW) {
        2588 throw Error('TaskQueue has started: ' + this);
        2589 }
        2590
        2591 if (task.queue) {
        2592 throw Error('Task is already scheduled in another queue');
        2593 }
        2594
        2595 if (task.isVolatile) {
        2596 this.volatileTasks_ = this.volatileTasks_ || [];
        2597 this.volatileTasks_.push(task);
        2598 }
        2599
        2600 this.tasks_.push(task);
        2601 task.queue = this;
        2602 task.promise[CANCEL_HANDLER_SYMBOL] =
        2603 this.onTaskCancelled_.bind(this, task);
        2604
        2605 vlog(1, () => this + '.enqueue(' + task + ')', this);
        2606 vlog(2, () => this.flow_.toString(), this);
        2607 },
        2608
        2609 /**
        2610 * Schedules the callbacks registered on the given promise in this queue.
        2611 *
        2612 * @param {!promise.Promise} promise the promise whose callbacks should be
        2613 * registered as interrupts in this task queue.
        2614 * @throws {Error} if this queue has already finished.
        2615 */
        2616 scheduleCallbacks: function(promise) {
        2617 if (this.state_ === TaskQueueState.FINISHED) {
        2618 throw new Error('cannot interrupt a finished q(' + this + ')');
        2619 }
        2620
        2621 if (this.pending_ && this.pending_.task.promise === promise) {
        2622 this.pending_.task.promise.queue_ = null;
        2623 this.pending_ = null;
        2624 asyncRun(this.executeNext_, this);
        2625 }
        2626
        2627 if (!promise.callbacks_) {
        2628 return;
        2629 }
        2630 promise.callbacks_.forEach(function(cb) {
        2631 cb.promise[CANCEL_HANDLER_SYMBOL] =
        2632 this.onTaskCancelled_.bind(this, cb);
        2633
        2634 cb.isVolatile = false;
        2635 if (cb.queue === this && this.tasks_.indexOf(cb) !== -1) {
        2636 return;
        2637 }
        2638
        2639 if (cb.queue) {
        2640 cb.queue.dropTask_(cb);
        2641 }
        2642
        2643 cb.queue = this;
        2644 if (!this.interrupts_) {
        2645 this.interrupts_ = [];
        2646 }
        2647 this.interrupts_.push(cb);
        2648 }, this);
        2649 promise.callbacks_ = null;
        2650 vlog(2, () => this + ' interrupted\n' + this.flow_, this);
        2651 },
        2652
        2653 /**
        2654 * Starts executing tasks in this queue. Once called, no further tasks may
        2655 * be {@linkplain #enqueue() enqueued} with this instance.
        2656 *
        2657 * @throws {Error} if this queue has already been started.
        2658 */
        2659 start: function() {
        2660 if (this.state_ !== TaskQueueState.NEW) {
        2661 throw new Error('TaskQueue has already started');
        2662 }
        2663 // Always asynchronously execute next, even if there doesn't look like
        2664 // there is anything in the queue. This will catch pending unhandled
        2665 // rejections that were registered before start was called.
        2666 asyncRun(this.executeNext_, this);
        2667 },
        2668
        2669 /**
        2670 * Aborts this task queue. If there are any scheduled tasks, they are silently
        2671 * cancelled and discarded (their callbacks will never fire). If this queue
        2672 * has a _pending_ task, the abortion error is used to cancel that task.
        2673 * Otherwise, this queue will emit an error event.
        2674 *
        2675 * @param {*} error The abortion reason.
        2676 * @private
        2677 */
        2678 abort_: function(error) {
        2679 var cancellation;
        2680
        2681 if (error instanceof FlowResetError) {
        2682 cancellation = error;
        2683 } else {
        2684 cancellation = new DiscardedTaskError(error);
        2685 }
        2686
        2687 if (this.interrupts_ && this.interrupts_.length) {
        2688 this.interrupts_.forEach((t) => t.reject(cancellation));
        2689 this.interrupts_ = [];
        2690 }
        2691
        2692 if (this.tasks_ && this.tasks_.length) {
        2693 this.tasks_.forEach((t) => t.reject(cancellation));
        2694 this.tasks_ = [];
        2695 }
        2696
        2697 if (this.pending_) {
        2698 vlog(2, () => this + '.abort(); cancelling pending task', this);
        2699 this.pending_.task.cancel(
        2700 /** @type {!webdriver.promise.CancellationError} */(error));
        2701
        2702 } else {
        2703 vlog(2, () => this + '.abort(); emitting error event', this);
        2704 this.emit('error', error, this);
        2705 }
        2706 },
        2707
        2708 /** @private */
        2709 executeNext_: function() {
        2710 if (this.state_ === TaskQueueState.FINISHED) {
        2711 return;
        2712 }
        2713 this.state_ = TaskQueueState.STARTED;
        2714 this.dropVolatileTasks_();
        2715
        2716 if (this.pending_ != null || this.processUnhandledRejections_()) {
        2717 return;
        2718 }
        2719
        2720 var task;
        2721 do {
        2722 task = this.getNextTask_();
        2723 } while (task && !task.promise.isPending());
        2724
        2725 if (!task) {
        2726 this.state_ = TaskQueueState.FINISHED;
        2727 this.tasks_ = [];
        2728 this.interrupts_ = null;
        2729 vlog(2, () => this + '.emit(end)', this);
        2730 this.emit('end', this);
        2731 return;
        2732 }
        2733
        2734 var self = this;
        2735 var subQ = new TaskQueue(this.flow_);
        2736 subQ.once('end', () => self.onTaskComplete_(result))
        2737 .once('error', (e) => self.onTaskFailure_(result, e));
        2738 vlog(2, () => self + ' created ' + subQ + ' for ' + task);
        2739
        2740 var result = undefined;
        2741 try {
        2742 this.pending_ = {task: task, q: subQ};
        2743 task.promise.queue_ = this;
        2744 result = subQ.execute_(task.execute);
        2745 subQ.start();
        2746 } catch (ex) {
        2747 subQ.abort_(ex);
        2748 }
        2749 },
        2750
        2751
        2752 /**
        2753 * @param {!Function} fn .
        2754 * @return {T} .
        2755 * @template T
        2756 * @private
        2757 */
        2758 execute_: function(fn) {
        2759 try {
        2760 activeFlows.push(this.flow_);
        2761 this.flow_.activeQueue_ = this;
        2762 return fn();
        2763 } finally {
        2764 this.flow_.activeQueue_ = null;
        2765 this.dropVolatileTasks_();
        2766 activeFlows.pop();
        2767 }
        2768 },
        2769
        2770 /**
        2771 * Process any unhandled rejections registered with this task queue. If there
        2772 * is a rejection, this queue will be aborted with the rejection error. If
        2773 * there are multiple rejections registered, this queue will be aborted with
        2774 * a {@link promise.MultipleUnhandledRejectionError}.
        2775 * @return {boolean} whether there was an unhandled rejection.
        2776 * @private
        2777 */
        2778 processUnhandledRejections_: function() {
        2779 if (!this.unhandledRejections_.size) {
        2780 return false;
        2781 }
        2782
        2783 var errors = new Set();
        2784 for (var rejection of this.unhandledRejections_) {
        2785 errors.add(rejection.value_);
        2786 }
        2787 this.unhandledRejections_.clear();
        2788
        2789 var errorToReport = errors.size === 1
        2790 ? errors.values().next().value
        2791 : new promise.MultipleUnhandledRejectionError(errors);
        2792
        2793 vlog(1, () => this + ' aborting due to unhandled rejections', this);
        2794 if (this.flow_.propagateUnhandledRejections_) {
        2795 this.abort_(errorToReport);
        2796 return true;
        2797 } else {
        2798 vlog(1, 'error propagation disabled; reporting to control flow');
        2799 this.flow_.reportUncaughtException_(errorToReport);
        2800 return false;
        2801 }
        2802 },
        2803
        2804 /**
        2805 * Drops any volatile tasks scheduled within this task queue.
        2806 * @private
        2807 */
        2808 dropVolatileTasks_: function() {
        2809 if (!this.volatileTasks_) {
        2810 return;
        2811 }
        2812 for (var task of this.volatileTasks_) {
        2813 if (task.isVolatile) {
        2814 vlog(2, () => this + ' dropping volatile task ' + task, this);
        2815 this.dropTask_(task);
        2816 }
        2817 }
        2818 this.volatileTasks_ = null;
        2819 },
        2820
        2821 /**
        2822 * @param {!Task} task The task to drop.
        2823 * @private
        2824 */
        2825 dropTask_: function(task) {
        2826 var index;
        2827 if (this.interrupts_) {
        2828 index = this.interrupts_.indexOf(task);
        2829 if (index != -1) {
        2830 task.queue = null;
        2831 this.interrupts_.splice(index, 1);
        2832 return;
        2833 }
        2834 }
        2835
        2836 index = this.tasks_.indexOf(task);
        2837 if (index != -1) {
        2838 task.queue = null;
        2839 this.tasks_.splice(index, 1);
        2840 }
        2841 },
        2842
        2843 /**
        2844 * @param {!Task} task The task that was cancelled.
        2845 * @param {!promise.CancellationError} reason The cancellation reason.
        2846 * @private
        2847 */
        2848 onTaskCancelled_: function(task, reason) {
        2849 if (this.pending_ && this.pending_.task === task) {
        2850 this.pending_.q.abort_(reason);
        2851 } else {
        2852 this.dropTask_(task);
        2853 }
        2854 },
        2855
        2856 /**
        2857 * @param {*} value the value originally returned by the task function.
        2858 * @private
        2859 */
        2860 onTaskComplete_: function(value) {
        2861 if (this.pending_) {
        2862 this.pending_.task.fulfill(value);
        2863 }
        2864 },
        2865
        2866 /**
        2867 * @param {*} taskFnResult the value originally returned by the task function.
        2868 * @param {*} error the error that caused the task function to terminate.
        2869 * @private
        2870 */
        2871 onTaskFailure_: function(taskFnResult, error) {
        2872 if (promise.Thenable.isImplementation(taskFnResult)) {
        2873 taskFnResult.cancel(promise.CancellationError.wrap(error));
        2874 }
        2875 this.pending_.task.reject(error);
        2876 },
        2877
        2878 /**
        2879 * @return {(Task|undefined)} the next task scheduled within this queue,
        2880 * if any.
        2881 * @private
        2882 */
        2883 getNextTask_: function() {
        2884 var task = undefined;
        2885 if (this.interrupts_) {
        2886 task = this.interrupts_.shift();
        2887 }
        2888 if (!task && this.tasks_) {
        2889 task = this.tasks_.shift();
        2890 }
        2891 return task;
        2892 }
        2893});
        2894
        2895
        2896
        2897/**
        2898 * The default flow to use if no others are active.
        2899 * @type {!promise.ControlFlow}
        2900 */
        2901var defaultFlow = new promise.ControlFlow();
        2902
        2903
        2904/**
        2905 * A stack of active control flows, with the top of the stack used to schedule
        2906 * commands. When there are multiple flows on the stack, the flow at index N
        2907 * represents a callback triggered within a task owned by the flow at index
        2908 * N-1.
        2909 * @type {!Array<!promise.ControlFlow>}
        2910 */
        2911var activeFlows = [];
        2912
        2913
        2914/**
        2915 * Changes the default flow to use when no others are active.
        2916 * @param {!promise.ControlFlow} flow The new default flow.
        2917 * @throws {Error} If the default flow is not currently active.
        2918 */
        2919promise.setDefaultFlow = function(flow) {
        2920 if (activeFlows.length) {
        2921 throw Error('You may only change the default flow while it is active');
        2922 }
        2923 defaultFlow = flow;
        2924};
        2925
        2926
        2927/**
        2928 * @return {!promise.ControlFlow} The currently active control flow.
        2929 */
        2930promise.controlFlow = function() {
        2931 return /** @type {!promise.ControlFlow} */ (
        2932 Arrays.peek(activeFlows) || defaultFlow);
        2933};
        2934
        2935
        2936/**
        2937 * Creates a new control flow. The provided callback will be invoked as the
        2938 * first task within the new flow, with the flow as its sole argument. Returns
        2939 * a promise that resolves to the callback result.
        2940 * @param {function(!promise.ControlFlow)} callback The entry point
        2941 * to the newly created flow.
        2942 * @return {!promise.Promise} A promise that resolves to the callback
        2943 * result.
        2944 */
        2945promise.createFlow = function(callback) {
        2946 var flow = new promise.ControlFlow;
        2947 return flow.execute(function() {
        2948 return callback(flow);
        2949 });
        2950};
        2951
        2952
        2953/**
        2954 * Tests is a function is a generator.
        2955 * @param {!Function} fn The function to test.
        2956 * @return {boolean} Whether the function is a generator.
        2957 */
        2958promise.isGenerator = function(fn) {
        2959 return fn.constructor.name === 'GeneratorFunction';
        2960};
        2961
        2962
        2963/**
        2964 * Consumes a {@code GeneratorFunction}. Each time the generator yields a
        2965 * promise, this function will wait for it to be fulfilled before feeding the
        2966 * fulfilled value back into {@code next}. Likewise, if a yielded promise is
        2967 * rejected, the rejection error will be passed to {@code throw}.
        2968 *
        2969 * __Example 1:__ the Fibonacci Sequence.
        2970 *
        2971 * promise.consume(function* fibonacci() {
        2972 * var n1 = 1, n2 = 1;
        2973 * for (var i = 0; i < 4; ++i) {
        2974 * var tmp = yield n1 + n2;
        2975 * n1 = n2;
        2976 * n2 = tmp;
        2977 * }
        2978 * return n1 + n2;
        2979 * }).then(function(result) {
        2980 * console.log(result); // 13
        2981 * });
        2982 *
        2983 * __Example 2:__ a generator that throws.
        2984 *
        2985 * promise.consume(function* () {
        2986 * yield promise.delayed(250).then(function() {
        2987 * throw Error('boom');
        2988 * });
        2989 * }).thenCatch(function(e) {
        2990 * console.log(e.toString()); // Error: boom
        2991 * });
        2992 *
        2993 * @param {!Function} generatorFn The generator function to execute.
        2994 * @param {Object=} opt_self The object to use as "this" when invoking the
        2995 * initial generator.
        2996 * @param {...*} var_args Any arguments to pass to the initial generator.
        2997 * @return {!promise.Promise<?>} A promise that will resolve to the
        2998 * generator's final result.
        2999 * @throws {TypeError} If the given function is not a generator.
        3000 */
        3001promise.consume = function(generatorFn, opt_self, var_args) {
        3002 if (!promise.isGenerator(generatorFn)) {
        3003 throw new TypeError('Input is not a GeneratorFunction: ' +
        3004 generatorFn.constructor.name);
        3005 }
        3006
        3007 var deferred = promise.defer();
        3008 var generator = generatorFn.apply(opt_self, Arrays.slice(arguments, 2));
        3009 callNext();
        3010 return deferred.promise;
        3011
        3012 /** @param {*=} opt_value . */
        3013 function callNext(opt_value) {
        3014 pump(generator.next, opt_value);
        3015 }
        3016
        3017 /** @param {*=} opt_error . */
        3018 function callThrow(opt_error) {
        3019 // Dictionary lookup required because Closure compiler's built-in
        3020 // externs does not include GeneratorFunction.prototype.throw.
        3021 pump(generator['throw'], opt_error);
        3022 }
        3023
        3024 function pump(fn, opt_arg) {
        3025 if (!deferred.isPending()) {
        3026 return; // Defererd was cancelled; silently abort.
        3027 }
        3028
        3029 try {
        3030 var result = fn.call(generator, opt_arg);
        3031 } catch (ex) {
        3032 deferred.reject(ex);
        3033 return;
        3034 }
        3035
        3036 if (result.done) {
        3037 deferred.fulfill(result.value);
        3038 return;
        3039 }
        3040
        3041 promise.asap(result.value, callNext, callThrow);
        3042 }
        3043};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/serializable.js.src.html b/docs/source/lib/webdriver/serializable.js.src.html new file mode 100644 index 0000000..e2a092f --- /dev/null +++ b/docs/source/lib/webdriver/serializable.js.src.html @@ -0,0 +1 @@ +serializable.js

        lib/webdriver/serializable.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18goog.provide('webdriver.Serializable');
        19
        20
        21
        22/**
        23 * Defines an object that can be asynchronously serialized to its WebDriver
        24 * wire representation.
        25 *
        26 * @constructor
        27 * @template T
        28 */
        29webdriver.Serializable = function() {};
        30
        31
        32/**
        33 * Returns either this instance's serialized represention, if immediately
        34 * available, or a promise for its serialized representation. This function is
        35 * conceptually equivalent to objects that have a {@code toJSON()} property,
        36 * except the serialize() result may be a promise or an object containing a
        37 * promise (which are not directly JSON friendly).
        38 *
        39 * @return {!(T|IThenable.<!T>)} This instance's serialized wire format.
        40 */
        41webdriver.Serializable.prototype.serialize = goog.abstractMethod;
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/session.js.src.html b/docs/source/lib/webdriver/session.js.src.html index bc91970..488aeef 100644 --- a/docs/source/lib/webdriver/session.js.src.html +++ b/docs/source/lib/webdriver/session.js.src.html @@ -1 +1 @@ -session.js

        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
        15goog.provide('webdriver.Session');
        16
        17goog.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 */
        28webdriver.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 */
        41webdriver.Session.prototype.getId = function() {
        42 return this.id_;
        43};
        44
        45
        46/**
        47 * @return {!webdriver.Capabilities} This session's capabilities.
        48 */
        49webdriver.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 */
        59webdriver.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 */
        69webdriver.Session.prototype.toJSON = function() {
        70 return this.getId();
        71};
        \ No newline at end of file +session.js

        lib/webdriver/session.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18goog.provide('webdriver.Session');
        19
        20goog.require('webdriver.Capabilities');
        21
        22
        23
        24/**
        25 * Contains information about a WebDriver session.
        26 * @param {string} id The session ID.
        27 * @param {!(Object|webdriver.Capabilities)} capabilities The session
        28 * capabilities.
        29 * @constructor
        30 */
        31webdriver.Session = function(id, capabilities) {
        32
        33 /** @private {string} */
        34 this.id_ = id;
        35
        36 /** @private {!webdriver.Capabilities} */
        37 this.caps_ = new webdriver.Capabilities().merge(capabilities);
        38};
        39
        40
        41/**
        42 * @return {string} This session's ID.
        43 */
        44webdriver.Session.prototype.getId = function() {
        45 return this.id_;
        46};
        47
        48
        49/**
        50 * @return {!webdriver.Capabilities} This session's capabilities.
        51 */
        52webdriver.Session.prototype.getCapabilities = function() {
        53 return this.caps_;
        54};
        55
        56
        57/**
        58 * Retrieves the value of a specific capability.
        59 * @param {string} key The capability to retrieve.
        60 * @return {*} The capability value.
        61 */
        62webdriver.Session.prototype.getCapability = function(key) {
        63 return this.caps_.get(key);
        64};
        65
        66
        67/**
        68 * Returns the JSON representation of this object, which is just the string
        69 * session ID.
        70 * @return {string} The JSON representation of this Session.
        71 */
        72webdriver.Session.prototype.toJSON = function() {
        73 return this.getId();
        74};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/stacktrace.js.src.html b/docs/source/lib/webdriver/stacktrace.js.src.html index 332f1ce..cf9311b 100644 --- a/docs/source/lib/webdriver/stacktrace.js.src.html +++ b/docs/source/lib/webdriver/stacktrace.js.src.html @@ -1 +1 @@ -stacktrace.js

        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
        22goog.provide('webdriver.stacktrace');
        23goog.provide('webdriver.stacktrace.Snapshot');
        24
        25goog.require('goog.array');
        26goog.require('goog.string');
        27goog.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 */
        38webdriver.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 */
        77webdriver.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 */
        87webdriver.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 */
        102webdriver.stacktrace.Snapshot.prototype.parsedStack_ = null;
        103
        104
        105/**
        106 * @return {!Array.<!webdriver.stacktrace.Frame>} The parsed stack trace.
        107 */
        108webdriver.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 */
        135webdriver.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 */
        174webdriver.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 */
        182webdriver.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 */
        190webdriver.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 */
        198webdriver.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 */
        206webdriver.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 */
        214webdriver.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 */
        225webdriver.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 */
        247webdriver.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 */
        256webdriver.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 */
        266webdriver.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 */
        278webdriver.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 */
        288webdriver.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 */
        298webdriver.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 */
        310webdriver.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 */
        324webdriver.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 */
        335webdriver.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 */
        348webdriver.stacktrace.V8_LOCATION_PATTERN_ = ' (?:\\((.*)\\)|(.*))';
        349
        350
        351/**
        352 * Regular expression for parsing one stack frame in V8.
        353 * @private {!RegExp}
        354 * @const
        355 */
        356webdriver.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 */
        368webdriver.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 */
        378webdriver.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 */
        388webdriver.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 */
        399webdriver.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 */
        411webdriver.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 */
        422webdriver.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 */
        434webdriver.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 */
        443webdriver.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 */
        454webdriver.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 */
        463webdriver.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 */
        472webdriver.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 */
        484webdriver.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 */
        497webdriver.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 */
        544webdriver.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 */
        569webdriver.stacktrace.getStack_ = function(error) {
        570 var stack = error.stack || error.stackTrace || '';
        571 var errorStr = error + '\n';
        572 if (goog.string.startsWith(stack, errorStr)) {
        573 stack = stack.substring(errorStr.length);
        574 }
        575 return stack;
        576};
        577
        578
        579/**
        580 * Formats an error's stack trace.
        581 * @param {!(Error|goog.testing.JsUnitException)} error The error to format.
        582 * @return {!(Error|goog.testing.JsUnitException)} The formatted error.
        583 */
        584webdriver.stacktrace.format = function(error) {
        585 var stack = webdriver.stacktrace.getStack_(error);
        586 var frames = webdriver.stacktrace.parse_(stack);
        587
        588 // Older versions of IE simply return [object Error] for toString(), so
        589 // only use that as a last resort.
        590 var errorStr = '';
        591 if (error.message) {
        592 errorStr = (error.name ? error.name + ': ' : '') + error.message;
        593 } else {
        594 errorStr = error.toString();
        595 }
        596
        597 // Ensure the error is in the V8 style with the error's string representation
        598 // prepended to the stack.
        599 error.stack = errorStr + '\n' + frames.join('\n');
        600 return error;
        601};
        602
        603
        604/**
        605 * Parses an Error object's stack trace.
        606 * @param {string} stack The stack trace.
        607 * @return {!Array.<!webdriver.stacktrace.Frame>} Stack frames. The
        608 * unrecognized frames will be nulled out.
        609 * @private
        610 */
        611webdriver.stacktrace.parse_ = function(stack) {
        612 if (!stack) {
        613 return [];
        614 }
        615
        616 var lines = stack.
        617 replace(/\s*$/, '').
        618 split('\n');
        619 var frames = [];
        620 for (var i = 0; i < lines.length; i++) {
        621 var frame = webdriver.stacktrace.parseStackFrame_(lines[i]);
        622 // The first two frames will be:
        623 // webdriver.stacktrace.Snapshot()
        624 // webdriver.stacktrace.get()
        625 // In the case of Opera, sometimes an extra frame is injected in the next
        626 // frame with a reported line number of zero. The next line detects that
        627 // case and skips that frame.
        628 if (!(goog.userAgent.OPERA && i == 2 && frame.getLine() == 0)) {
        629 frames.push(frame || webdriver.stacktrace.ANONYMOUS_FRAME_);
        630 }
        631 }
        632 return frames;
        633};
        634
        635
        636/**
        637 * Gets the native stack trace if available otherwise follows the call chain.
        638 * The generated trace will exclude all frames up to and including the call to
        639 * this function.
        640 * @return {!Array.<!webdriver.stacktrace.Frame>} The frames of the stack trace.
        641 */
        642webdriver.stacktrace.get = function() {
        643 return new webdriver.stacktrace.Snapshot(1).getStacktrace();
        644};
        \ No newline at end of file +stacktrace.js

        lib/webdriver/stacktrace.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Tools for parsing and pretty printing error stack traces. This
        20 * file is based on goog.testing.stacktrace.
        21 */
        22
        23goog.provide('webdriver.stacktrace');
        24goog.provide('webdriver.stacktrace.Snapshot');
        25
        26goog.require('goog.array');
        27goog.require('goog.string');
        28goog.require('goog.userAgent');
        29
        30
        31
        32/**
        33 * Stores a snapshot of the stack trace at the time this instance was created.
        34 * The stack trace will always be adjusted to exclude this function call.
        35 * @param {number=} opt_slice The number of frames to remove from the top of
        36 * the generated stack trace.
        37 * @constructor
        38 */
        39webdriver.stacktrace.Snapshot = function(opt_slice) {
        40
        41 /** @private {number} */
        42 this.slice_ = opt_slice || 0;
        43
        44 var error;
        45 if (webdriver.stacktrace.CAN_CAPTURE_STACK_TRACE_) {
        46 error = Error();
        47 Error.captureStackTrace(error, webdriver.stacktrace.Snapshot);
        48 } else {
        49 // Remove 1 extra frame for the call to this constructor.
        50 this.slice_ += 1;
        51 // IE will only create a stack trace when the Error is thrown.
        52 // We use null.x() to throw an exception instead of throw this.error_
        53 // because the closure compiler may optimize throws to a function call
        54 // in an attempt to minimize the binary size which in turn has the side
        55 // effect of adding an unwanted stack frame.
        56 try {
        57 null.x();
        58 } catch (e) {
        59 error = e;
        60 }
        61 }
        62
        63 /**
        64 * The error's stacktrace.
        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 */
        77webdriver.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 */
        87webdriver.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 */
        102webdriver.stacktrace.Snapshot.prototype.parsedStack_ = null;
        103
        104
        105/**
        106 * @return {!Array.<!webdriver.stacktrace.Frame>} The parsed stack trace.
        107 */
        108webdriver.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 */
        135webdriver.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 */
        174webdriver.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 */
        182webdriver.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 */
        190webdriver.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 */
        198webdriver.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 */
        206webdriver.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 */
        214webdriver.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 */
        225webdriver.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 */
        247webdriver.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 */
        256webdriver.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 */
        266webdriver.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 */
        278webdriver.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 */
        288webdriver.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 */
        298webdriver.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 */
        310webdriver.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 */
        324webdriver.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 */
        335webdriver.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 */
        348webdriver.stacktrace.V8_LOCATION_PATTERN_ = ' (?:\\((.*)\\)|(.*))';
        349
        350
        351/**
        352 * Regular expression for parsing one stack frame in V8.
        353 * @private {!RegExp}
        354 * @const
        355 */
        356webdriver.stacktrace.V8_STACK_FRAME_REGEXP_ = new RegExp('^\\s+at' +
        357 // Prevent intersections with IE10 stack frame regex.
        358 '(?! (?:Anonymous function|Global code|eval code) )' +
        359 '(?:' + webdriver.stacktrace.V8_FUNCTION_CALL_PATTERN_ + ')?' +
        360 webdriver.stacktrace.V8_LOCATION_PATTERN_ + '$');
        361
        362
        363/**
        364 * RegExp pattern for function names in the Firefox stack trace.
        365 * Firefox has extended identifiers to deal with inner functions and anonymous
        366 * functions: https://bugzilla.mozilla.org/show_bug.cgi?id=433529#c9
        367 * @private {string}
        368 * @const
        369 */
        370webdriver.stacktrace.FIREFOX_FUNCTION_NAME_PATTERN_ =
        371 webdriver.stacktrace.IDENTIFIER_PATTERN_ + '[\\w./<$]*';
        372
        373
        374/**
        375 * RegExp pattern for function call in the Firefox stack trace.
        376 * Creates a submatch for the function name.
        377 * @private {string}
        378 * @const
        379 */
        380webdriver.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ =
        381 '(' + webdriver.stacktrace.FIREFOX_FUNCTION_NAME_PATTERN_ + ')?' +
        382 '(?:\\(.*\\))?@';
        383
        384
        385/**
        386 * Regular expression for parsing one stack frame in Firefox.
        387 * @private {!RegExp}
        388 * @const
        389 */
        390webdriver.stacktrace.FIREFOX_STACK_FRAME_REGEXP_ = new RegExp('^' +
        391 webdriver.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ +
        392 '(?::0|' + webdriver.stacktrace.URL_PATTERN_ + ')$');
        393
        394
        395/**
        396 * RegExp pattern for function call in a Chakra (IE) stack trace. This
        397 * expression creates 2 submatches on the (optional) context and function name,
        398 * matching identifiers like 'foo.Bar.prototype.baz', 'Anonymous function',
        399 * 'eval code', and 'Global code'.
        400 * @private {string}
        401 * @const
        402 */
        403webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ =
        404 '(?:(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ +
        405 '(?:\\.' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')*)\\.)?' +
        406 '(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)';
        407
        408
        409/**
        410 * Regular expression for parsing on stack frame in Chakra (IE).
        411 * @private {!RegExp}
        412 * @const
        413 */
        414webdriver.stacktrace.CHAKRA_STACK_FRAME_REGEXP_ = new RegExp('^ at ' +
        415 webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ +
        416 '\\s*(?:\\((.*)\\))$');
        417
        418
        419/**
        420 * Placeholder for an unparsable frame in a stack trace generated by
        421 * {@link goog.testing.stacktrace}.
        422 * @private {string}
        423 * @const
        424 */
        425webdriver.stacktrace.UNKNOWN_CLOSURE_FRAME_ = '> (unknown)';
        426
        427
        428/**
        429 * Representation of an anonymous frame in a stack trace generated by
        430 * {@link goog.testing.stacktrace}.
        431 * @private {string}
        432 * @const
        433 */
        434webdriver.stacktrace.ANONYMOUS_CLOSURE_FRAME_ = '> anonymous';
        435
        436
        437/**
        438 * Pattern for a function call in a Closure stack trace. Creates three optional
        439 * submatches: the context, function name, and alias.
        440 * @private {string}
        441 * @const
        442 */
        443webdriver.stacktrace.CLOSURE_FUNCTION_CALL_PATTERN_ =
        444 webdriver.stacktrace.QUALIFIED_NAME_PATTERN_ +
        445 '(?:\\(.*\\))?' + // Ignore arguments if present.
        446 webdriver.stacktrace.V8_ALIAS_PATTERN_;
        447
        448
        449/**
        450 * Regular expression for parsing a stack frame generated by Closure's
        451 * {@link goog.testing.stacktrace}.
        452 * @private {!RegExp}
        453 * @const
        454 */
        455webdriver.stacktrace.CLOSURE_STACK_FRAME_REGEXP_ = new RegExp('^> ' +
        456 '(?:' + webdriver.stacktrace.CLOSURE_FUNCTION_CALL_PATTERN_ +
        457 '(?: at )?)?' +
        458 '(?:(.*:\\d+:\\d+)|' + webdriver.stacktrace.URL_PATTERN_ + ')?$');
        459
        460
        461/**
        462 * Parses one stack frame.
        463 * @param {string} frameStr The stack frame as string.
        464 * @return {webdriver.stacktrace.Frame} Stack frame object or null if the
        465 * parsing failed.
        466 * @private
        467 */
        468webdriver.stacktrace.parseStackFrame_ = function(frameStr) {
        469 var m = frameStr.match(webdriver.stacktrace.V8_STACK_FRAME_REGEXP_);
        470 if (m) {
        471 return new webdriver.stacktrace.Frame(
        472 m[1] || m[2], m[3], m[4], m[5] || m[6]);
        473 }
        474
        475 if (frameStr.length >
        476 webdriver.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_) {
        477 return webdriver.stacktrace.parseLongFirefoxFrame_(frameStr);
        478 }
        479
        480 m = frameStr.match(webdriver.stacktrace.FIREFOX_STACK_FRAME_REGEXP_);
        481 if (m) {
        482 return new webdriver.stacktrace.Frame('', m[1], '', m[2]);
        483 }
        484
        485 m = frameStr.match(webdriver.stacktrace.CHAKRA_STACK_FRAME_REGEXP_);
        486 if (m) {
        487 return new webdriver.stacktrace.Frame(m[1], m[2], '', m[3]);
        488 }
        489
        490 if (frameStr == webdriver.stacktrace.UNKNOWN_CLOSURE_FRAME_ ||
        491 frameStr == webdriver.stacktrace.ANONYMOUS_CLOSURE_FRAME_) {
        492 return webdriver.stacktrace.ANONYMOUS_FRAME_;
        493 }
        494
        495 m = frameStr.match(webdriver.stacktrace.CLOSURE_STACK_FRAME_REGEXP_);
        496 if (m) {
        497 return new webdriver.stacktrace.Frame(m[1], m[2], m[3], m[4] || m[5]);
        498 }
        499
        500 return null;
        501};
        502
        503
        504/**
        505 * Parses a long firefox stack frame.
        506 * @param {string} frameStr The stack frame as string.
        507 * @return {!webdriver.stacktrace.Frame} Stack frame object.
        508 * @private
        509 */
        510webdriver.stacktrace.parseLongFirefoxFrame_ = function(frameStr) {
        511 var firstParen = frameStr.indexOf('(');
        512 var lastAmpersand = frameStr.lastIndexOf('@');
        513 var lastColon = frameStr.lastIndexOf(':');
        514 var functionName = '';
        515 if ((firstParen >= 0) && (firstParen < lastAmpersand)) {
        516 functionName = frameStr.substring(0, firstParen);
        517 }
        518 var loc = '';
        519 if ((lastAmpersand >= 0) && (lastAmpersand + 1 < lastColon)) {
        520 loc = frameStr.substring(lastAmpersand + 1);
        521 }
        522 return new webdriver.stacktrace.Frame('', functionName, '', loc);
        523};
        524
        525
        526/**
        527 * Get an error's stack trace with the error string trimmed.
        528 * V8 prepends the string representation of an error to its stack trace.
        529 * This function trims the string so that the stack trace can be parsed
        530 * consistently with the other JS engines.
        531 * @param {(Error|goog.testing.JsUnitException)} error The error.
        532 * @return {string} The stack trace string.
        533 */
        534webdriver.stacktrace.getStack = function(error) {
        535 if (!error) {
        536 return '';
        537 }
        538 var stack = error.stack || error.stackTrace || '';
        539 var errorStr = error + '\n';
        540 if (goog.string.startsWith(stack, errorStr)) {
        541 stack = stack.substring(errorStr.length);
        542 }
        543 return stack;
        544};
        545
        546
        547/**
        548 * Formats an error's stack trace.
        549 * @param {!(Error|goog.testing.JsUnitException)} error The error to format.
        550 * @return {!(Error|goog.testing.JsUnitException)} The formatted error.
        551 */
        552webdriver.stacktrace.format = function(error) {
        553 var stack = webdriver.stacktrace.getStack(error);
        554 var frames = webdriver.stacktrace.parse_(stack);
        555
        556 // If the original stack is in an unexpected format, our formatted stack
        557 // trace will be a bunch of " at <anonymous>" lines. If this is the case,
        558 // just return the error unmodified to avoid losing information. This is
        559 // necessary since the user may have defined a custom stack formatter in
        560 // V8 via Error.prepareStackTrace. See issue 7994.
        561 var isAnonymousFrame = function(frame) {
        562 return frame.toString() === ' at <anonymous>';
        563 };
        564 if (frames.length && goog.array.every(frames, isAnonymousFrame)) {
        565 return error;
        566 }
        567
        568 // Older versions of IE simply return [object Error] for toString(), so
        569 // only use that as a last resort.
        570 var errorStr = '';
        571 if (error.message) {
        572 errorStr = (error.name ? error.name + ': ' : '') + error.message;
        573 } else {
        574 errorStr = error.toString();
        575 }
        576
        577 // Ensure the error is in the V8 style with the error's string representation
        578 // prepended to the stack.
        579 error.stack = errorStr + '\n' + frames.join('\n');
        580 return error;
        581};
        582
        583
        584/**
        585 * Parses an Error object's stack trace.
        586 * @param {string} stack The stack trace.
        587 * @return {!Array.<!webdriver.stacktrace.Frame>} Stack frames. The
        588 * unrecognized frames will be nulled out.
        589 * @private
        590 */
        591webdriver.stacktrace.parse_ = function(stack) {
        592 if (!stack) {
        593 return [];
        594 }
        595
        596 var lines = stack.
        597 replace(/\s*$/, '').
        598 split('\n');
        599 var frames = [];
        600 for (var i = 0; i < lines.length; i++) {
        601 var frame = webdriver.stacktrace.parseStackFrame_(lines[i]);
        602 // The first two frames will be:
        603 // webdriver.stacktrace.Snapshot()
        604 // webdriver.stacktrace.get()
        605 frames.push(frame || webdriver.stacktrace.ANONYMOUS_FRAME_);
        606 }
        607 return frames;
        608};
        609
        610
        611/**
        612 * Gets the native stack trace if available otherwise follows the call chain.
        613 * The generated trace will exclude all frames up to and including the call to
        614 * this function.
        615 * @return {!Array.<!webdriver.stacktrace.Frame>} The frames of the stack trace.
        616 */
        617webdriver.stacktrace.get = function() {
        618 return new webdriver.stacktrace.Snapshot(1).getStacktrace();
        619};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/testing/asserts.js.src.html b/docs/source/lib/webdriver/testing/asserts.js.src.html index 5d37bbd..328b759 100644 --- a/docs/source/lib/webdriver/testing/asserts.js.src.html +++ b/docs/source/lib/webdriver/testing/asserts.js.src.html @@ -1 +1 @@ -asserts.js

        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
        20goog.provide('webdriver.testing.Assertion');
        21goog.provide('webdriver.testing.ContainsMatcher');
        22goog.provide('webdriver.testing.NegatedAssertion');
        23goog.provide('webdriver.testing.assert');
        24goog.provide('webdriver.testing.asserts');
        25
        26goog.require('goog.array');
        27goog.require('goog.labs.testing.CloseToMatcher');
        28goog.require('goog.labs.testing.EndsWithMatcher');
        29goog.require('goog.labs.testing.EqualToMatcher');
        30goog.require('goog.labs.testing.EqualsMatcher');
        31goog.require('goog.labs.testing.GreaterThanEqualToMatcher');
        32goog.require('goog.labs.testing.GreaterThanMatcher');
        33goog.require('goog.labs.testing.LessThanEqualToMatcher');
        34goog.require('goog.labs.testing.LessThanMatcher');
        35goog.require('goog.labs.testing.InstanceOfMatcher');
        36goog.require('goog.labs.testing.IsNotMatcher');
        37goog.require('goog.labs.testing.IsNullMatcher');
        38goog.require('goog.labs.testing.IsNullOrUndefinedMatcher');
        39goog.require('goog.labs.testing.IsUndefinedMatcher');
        40goog.require('goog.labs.testing.Matcher');
        41goog.require('goog.labs.testing.ObjectEqualsMatcher');
        42goog.require('goog.labs.testing.RegexMatcher');
        43goog.require('goog.labs.testing.StartsWithMatcher');
        44goog.require('goog.labs.testing.assertThat');
        45goog.require('goog.string');
        46goog.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 */
        55webdriver.testing.ContainsMatcher = function(value) {
        56 /** @private {*} */
        57 this.value_ = value;
        58};
        59
        60
        61/** @override */
        62webdriver.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 */
        74webdriver.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 */
        87webdriver.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 */
        120webdriver.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 */
        146webdriver.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 */
        167webdriver.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 */
        182webdriver.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 */
        197webdriver.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 */
        211webdriver.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 */
        228webdriver.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 */
        242webdriver.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 */
        254webdriver.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 */
        265webdriver.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 */
        276webdriver.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 */
        291webdriver.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 */
        304webdriver.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 */
        318webdriver.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 */
        332webdriver.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 */
        345webdriver.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 */
        354webdriver.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 */
        363webdriver.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 */
        375webdriver.testing.NegatedAssertion = function(value) {
        376 goog.base(this, value);
        377 this.value = value;
        378};
        379goog.inherits(
        380 webdriver.testing.NegatedAssertion, webdriver.testing.Assertion);
        381
        382
        383/** @override */
        384webdriver.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 */
        397webdriver.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 */
        411webdriver.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 */
        444webdriver.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 */
        465webdriver.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
        477goog.exportSymbol('assertThat', webdriver.testing.asserts.assertThat);
        478// Mappings for goog.labs.testing matcher functions to the legacy
        479// webdriver.testing.asserts matchers.
        480goog.exportSymbol('contains', containsString);
        481goog.exportSymbol('equalTo', webdriver.testing.asserts.equalTo);
        482goog.exportSymbol('equals', webdriver.testing.asserts.equalTo);
        483goog.exportSymbol('is', webdriver.testing.asserts.equalTo);
        484goog.exportSymbol('not', isNot);
        485goog.exportSymbol('or', anyOf);
        \ No newline at end of file +asserts.js

        lib/webdriver/testing/asserts.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Assertions and expectation utilities for use in WebDriver test
        20 * cases.
        21 */
        22
        23goog.provide('webdriver.testing.Assertion');
        24goog.provide('webdriver.testing.ContainsMatcher');
        25goog.provide('webdriver.testing.NegatedAssertion');
        26goog.provide('webdriver.testing.assert');
        27goog.provide('webdriver.testing.asserts');
        28
        29goog.require('goog.array');
        30goog.require('goog.labs.testing.CloseToMatcher');
        31goog.require('goog.labs.testing.EndsWithMatcher');
        32goog.require('goog.labs.testing.EqualToMatcher');
        33goog.require('goog.labs.testing.EqualsMatcher');
        34goog.require('goog.labs.testing.GreaterThanEqualToMatcher');
        35goog.require('goog.labs.testing.GreaterThanMatcher');
        36goog.require('goog.labs.testing.LessThanEqualToMatcher');
        37goog.require('goog.labs.testing.LessThanMatcher');
        38goog.require('goog.labs.testing.InstanceOfMatcher');
        39goog.require('goog.labs.testing.IsNotMatcher');
        40goog.require('goog.labs.testing.IsNullMatcher');
        41goog.require('goog.labs.testing.IsNullOrUndefinedMatcher');
        42goog.require('goog.labs.testing.IsUndefinedMatcher');
        43goog.require('goog.labs.testing.Matcher');
        44goog.require('goog.labs.testing.ObjectEqualsMatcher');
        45goog.require('goog.labs.testing.RegexMatcher');
        46goog.require('goog.labs.testing.StartsWithMatcher');
        47goog.require('goog.labs.testing.assertThat');
        48goog.require('goog.string');
        49goog.require('webdriver.promise');
        50
        51
        52/**
        53 * Accepts strins or array-like structures that contain {@code value}.
        54 * @param {*} value The value to check for.
        55 * @constructor
        56 * @implements {goog.labs.testing.Matcher}
        57 */
        58webdriver.testing.ContainsMatcher = function(value) {
        59 /** @private {*} */
        60 this.value_ = value;
        61};
        62
        63
        64/** @override */
        65webdriver.testing.ContainsMatcher.prototype.matches = function(actualValue) {
        66 if (goog.isString(actualValue)) {
        67 return goog.string.contains(
        68 actualValue, /** @type {string} */(this.value_));
        69 } else {
        70 return goog.array.contains(
        71 /** @type {goog.array.ArrayLike} */(actualValue), this.value_);
        72 }
        73};
        74
        75
        76/** @override */
        77webdriver.testing.ContainsMatcher.prototype.describe = function(actualValue) {
        78 return actualValue + ' does not contain ' + this.value_;
        79};
        80
        81
        82
        83/**
        84 * Utility for performing assertions against a given {@code value}. If the
        85 * value is a {@link webdriver.promise.Promise}, this assertion will wait
        86 * for it to resolve before applying any matchers.
        87 * @param {*} value The value to wrap and apply matchers to.
        88 * @constructor
        89 */
        90webdriver.testing.Assertion = function(value) {
        91
        92 /** @private {*} */
        93 this.value_ = value;
        94
        95 if (!(this instanceof webdriver.testing.NegatedAssertion)) {
        96 /**
        97 * A self reference provided for writing fluent assertions:
        98 * webdriver.testing.assert(x).is.equalTo(y);
        99 * @type {!webdriver.testing.Assertion}
        100 */
        101 this.is = this;
        102
        103 /**
        104 * Negates any matchers applied to this instance's value:
        105 * webdriver.testing.assert(x).not.equalTo(y);
        106 * @type {!webdriver.testing.NegatedAssertion}
        107 */
        108 this.not = new webdriver.testing.NegatedAssertion(value);
        109 }
        110};
        111
        112
        113/**
        114 * Wraps an object literal implementing the Matcher interface. This is used
        115 * to appease the Closure compiler, which will not treat an object literal as
        116 * implementing an interface.
        117 * @param {{matches: function(*): boolean, describe: function(): string}} obj
        118 * The object literal to delegate to.
        119 * @constructor
        120 * @implements {goog.labs.testing.Matcher}
        121 * @private
        122 */
        123webdriver.testing.Assertion.DelegatingMatcher_ = function(obj) {
        124
        125 /** @override */
        126 this.matches = function(value) {
        127 return obj.matches(value);
        128 };
        129
        130 /** @override */
        131 this.describe = function() {
        132 return obj.describe();
        133 };
        134};
        135
        136
        137/**
        138 * Asserts that the given {@code matcher} accepts the value wrapped by this
        139 * instance. If the wrapped value is a promise, this function will defer
        140 * applying the assertion until the value has been resolved. Otherwise, it
        141 * will be applied immediately.
        142 * @param {!goog.labs.testing.Matcher} matcher The matcher to apply
        143 * @param {string=} opt_message A message to include if the matcher does not
        144 * accept the value wrapped by this assertion.
        145 * @return {webdriver.promise.Promise} The deferred assertion result, or
        146 * {@code null} if the assertion was immediately applied.
        147 * @protected
        148 */
        149webdriver.testing.Assertion.prototype.apply = function(matcher, opt_message) {
        150 var result = null;
        151 if (webdriver.promise.isPromise(this.value_)) {
        152 result = webdriver.promise.when(this.value_, function(value) {
        153 goog.labs.testing.assertThat(value, matcher, opt_message);
        154 });
        155 } else {
        156 goog.labs.testing.assertThat(this.value_, matcher, opt_message);
        157 }
        158 return result;
        159};
        160
        161
        162/**
        163 * Asserts that the value managed by this assertion is a number strictly
        164 * greater than {@code value}.
        165 * @param {number} value The minimum value.
        166 * @param {string=} opt_message A message to include if the matcher does not
        167 * accept the value wrapped by this assertion.
        168 * @return {webdriver.promise.Promise} The assertion result.
        169 */
        170webdriver.testing.Assertion.prototype.greaterThan = function(
        171 value, opt_message) {
        172 return this.apply(
        173 new goog.labs.testing.GreaterThanMatcher(value), opt_message);
        174};
        175
        176
        177/**
        178 * Asserts that the value managed by this assertion is a number >= the given
        179 * value.
        180 * @param {number} value The minimum value.
        181 * @param {string=} opt_message A message to include if the matcher does not
        182 * accept the value wrapped by this assertion.
        183 * @return {webdriver.promise.Promise} The assertion result.
        184 */
        185webdriver.testing.Assertion.prototype.greaterThanEqualTo = function(
        186 value, opt_message) {
        187 return this.apply(
        188 new goog.labs.testing.GreaterThanEqualToMatcher(value), opt_message);
        189};
        190
        191
        192/**
        193 * Asserts that the value managed by this assertion is a number strictly less
        194 * than the given value.
        195 * @param {number} value The maximum value.
        196 * @param {string=} opt_message A message to include if the matcher does not
        197 * accept the value wrapped by this assertion.
        198 * @return {webdriver.promise.Promise} The assertion result.
        199 */
        200webdriver.testing.Assertion.prototype.lessThan = function(value, opt_message) {
        201 return this.apply(
        202 new goog.labs.testing.LessThanMatcher(value), opt_message);
        203};
        204
        205
        206/**
        207 * Asserts that the value managed by this assertion is a number <= the given
        208 * value.
        209 * @param {number} value The maximum value.
        210 * @param {string=} opt_message A message to include if the matcher does not
        211 * accept the value wrapped by this assertion.
        212 * @return {webdriver.promise.Promise} The assertion result.
        213 */
        214webdriver.testing.Assertion.prototype.lessThanEqualTo = function(
        215 value, opt_message) {
        216 return this.apply(
        217 new goog.labs.testing.LessThanEqualToMatcher(value), opt_message);
        218};
        219
        220
        221/**
        222 * Asserts that the wrapped value is a number within a given distance of an
        223 * expected value.
        224 * @param {number} value The expected value.
        225 * @param {number} range The maximum amount the actual value is permitted to
        226 * differ from the expected value.
        227 * @param {string=} opt_message A message to include if the matcher does not
        228 * accept the value wrapped by this assertion.
        229 * @return {webdriver.promise.Promise} The assertion result.
        230 */
        231webdriver.testing.Assertion.prototype.closeTo = function(
        232 value, range, opt_message) {
        233 return this.apply(
        234 new goog.labs.testing.CloseToMatcher(value, range), opt_message);
        235};
        236
        237
        238/**
        239 * Asserts that the wrapped value is an instance of the given class.
        240 * @param {!Function} ctor The expected class constructor.
        241 * @param {string=} opt_message A message to include if the matcher does not
        242 * accept the value wrapped by this assertion.
        243 * @return {webdriver.promise.Promise} The assertion result.
        244 */
        245webdriver.testing.Assertion.prototype.instanceOf = function(ctor, opt_message) {
        246 return this.apply(
        247 new goog.labs.testing.InstanceOfMatcher(ctor), opt_message);
        248};
        249
        250
        251/**
        252 * Asserts that the wrapped value is null.
        253 * @param {string=} opt_message A message to include if the matcher does not
        254 * accept the value wrapped by this assertion.
        255 * @return {webdriver.promise.Promise} The assertion result.
        256 */
        257webdriver.testing.Assertion.prototype.isNull = function(opt_message) {
        258 return this.apply(new goog.labs.testing.IsNullMatcher(), opt_message);
        259};
        260
        261
        262/**
        263 * Asserts that the wrapped value is undefined.
        264 * @param {string=} opt_message A message to include if the matcher does not
        265 * accept the value wrapped by this assertion.
        266 * @return {webdriver.promise.Promise} The assertion result.
        267 */
        268webdriver.testing.Assertion.prototype.isUndefined = function(opt_message) {
        269 return this.apply(new goog.labs.testing.IsUndefinedMatcher(), opt_message);
        270};
        271
        272
        273/**
        274 * Asserts that the wrapped value is null or undefined.
        275 * @param {string=} opt_message A message to include if the matcher does not
        276 * accept the value wrapped by this assertion.
        277 * @return {webdriver.promise.Promise} The assertion result.
        278 */
        279webdriver.testing.Assertion.prototype.isNullOrUndefined = function(
        280 opt_message) {
        281 return this.apply(
        282 new goog.labs.testing.IsNullOrUndefinedMatcher(), opt_message);
        283};
        284
        285
        286/**
        287 * Asserts that the wrapped value is a string or array-like structure
        288 * containing the given value.
        289 * @param {*} value The expected value.
        290 * @param {string=} opt_message A message to include if the matcher does not
        291 * accept the value wrapped by this assertion.
        292 * @return {webdriver.promise.Promise} The assertion result.
        293 */
        294webdriver.testing.Assertion.prototype.contains = function(value, opt_message) {
        295 return this.apply(
        296 new webdriver.testing.ContainsMatcher(value), opt_message);
        297};
        298
        299
        300/**
        301 * Asserts that the wrapped value is a string ending with the given suffix.
        302 * @param {string} suffix The expected suffix.
        303 * @param {string=} opt_message A message to include if the matcher does not
        304 * accept the value wrapped by this assertion.
        305 * @return {webdriver.promise.Promise} The assertion result.
        306 */
        307webdriver.testing.Assertion.prototype.endsWith = function(
        308 suffix, opt_message) {
        309 return this.apply(
        310 new goog.labs.testing.EndsWithMatcher(suffix), opt_message);
        311};
        312
        313
        314/**
        315 * Asserts that the wrapped value is a string starting with the given prefix.
        316 * @param {string} prefix The expected prefix.
        317 * @param {string=} opt_message A message to include if the matcher does not
        318 * accept the value wrapped by this assertion.
        319 * @return {webdriver.promise.Promise} The assertion result.
        320 */
        321webdriver.testing.Assertion.prototype.startsWith = function(
        322 prefix, opt_message) {
        323 return this.apply(
        324 new goog.labs.testing.StartsWithMatcher(prefix), opt_message);
        325};
        326
        327
        328/**
        329 * Asserts that the wrapped value is a string that matches the given RegExp.
        330 * @param {!RegExp} regex The regex to test.
        331 * @param {string=} opt_message A message to include if the matcher does not
        332 * accept the value wrapped by this assertion.
        333 * @return {webdriver.promise.Promise} The assertion result.
        334 */
        335webdriver.testing.Assertion.prototype.matches = function(regex, opt_message) {
        336 return this.apply(new goog.labs.testing.RegexMatcher(regex), opt_message);
        337};
        338
        339
        340/**
        341 * Asserts that the value managed by this assertion is strictly equal to the
        342 * given {@code value}.
        343 * @param {*} value The expected value.
        344 * @param {string=} opt_message A message to include if the matcher does not
        345 * accept the value wrapped by this assertion.
        346 * @return {webdriver.promise.Promise} The assertion result.
        347 */
        348webdriver.testing.Assertion.prototype.equalTo = function(value, opt_message) {
        349 return this.apply(webdriver.testing.asserts.equalTo(value), opt_message);
        350};
        351
        352
        353/**
        354 * Asserts that the value managed by this assertion is strictly true.
        355 * @return {webdriver.promise.Promise} The assertion result.
        356 */
        357webdriver.testing.Assertion.prototype.isTrue = function() {
        358 return this.equalTo(true);
        359};
        360
        361
        362/**
        363 * Asserts that the value managed by this assertion is strictly false.
        364 * @return {webdriver.promise.Promise} The assertion result.
        365 */
        366webdriver.testing.Assertion.prototype.isFalse = function() {
        367 return this.equalTo(false);
        368};
        369
        370
        371
        372/**
        373 * An assertion that negates any applied matchers.
        374 * @param {*} value The value to perform assertions on.
        375 * @constructor
        376 * @extends {webdriver.testing.Assertion}
        377 */
        378webdriver.testing.NegatedAssertion = function(value) {
        379 webdriver.testing.NegatedAssertion.base(this, 'constructor', value);
        380 this.value = value;
        381};
        382goog.inherits(
        383 webdriver.testing.NegatedAssertion, webdriver.testing.Assertion);
        384
        385
        386/** @override */
        387webdriver.testing.NegatedAssertion.prototype.apply = function(
        388 matcher, opt_message) {
        389 matcher = new goog.labs.testing.IsNotMatcher(matcher);
        390 return webdriver.testing.NegatedAssertion.base(this, 'apply', matcher,
        391 opt_message);
        392};
        393
        394/**
        395 * Creates a new assertion.
        396 * @param {*} value The value to perform an assertion on.
        397 * @return {!webdriver.testing.Assertion} The new assertion.
        398 */
        399webdriver.testing.assert = function(value) {
        400 return new webdriver.testing.Assertion(value);
        401};
        402
        403
        404/**
        405 * Registers a new assertion to expose from the
        406 * {@link webdriver.testing.Assertion} prototype.
        407 * @param {string} name The assertion name.
        408 * @param {(function(new: goog.labs.testing.Matcher, *)|
        409 * {matches: function(*): boolean,
        410 * describe: function(): string})} matcherTemplate Either the
        411 * matcher constructor to use, or an object literal defining a matcher.
        412 */
        413webdriver.testing.assert.register = function(name, matcherTemplate) {
        414 webdriver.testing.Assertion.prototype[name] = function(value, opt_message) {
        415 var matcher;
        416 if (goog.isFunction(matcherTemplate)) {
        417 var ctor = /** @type {function(new: goog.labs.testing.Matcher, *)} */ (
        418 matcherTemplate);
        419 matcher = new ctor(value);
        420 } else {
        421 matcher = new webdriver.testing.Assertion.DelegatingMatcher_(value);
        422 }
        423 return this.apply(matcher, opt_message);
        424 };
        425};
        426
        427
        428/**
        429 * Asserts that a matcher accepts a given value. This function has two
        430 * signatures based on the number of arguments:
        431 *
        432 * Two arguments:
        433 * assertThat(actualValue, matcher)
        434 * Three arguments:
        435 * assertThat(failureMessage, actualValue, matcher)
        436 *
        437 * @param {*} failureMessageOrActualValue Either a failure message or the value
        438 * to apply to the given matcher.
        439 * @param {*} actualValueOrMatcher Either the value to apply to the given
        440 * matcher, or the matcher itself.
        441 * @param {goog.labs.testing.Matcher=} opt_matcher The matcher to use;
        442 * ignored unless this function is invoked with three arguments.
        443 * @return {!webdriver.promise.Promise} The assertion result.
        444 * @deprecated Use webdriver.testing.asserts.assert instead.
        445 */
        446webdriver.testing.asserts.assertThat = function(
        447 failureMessageOrActualValue, actualValueOrMatcher, opt_matcher) {
        448 var args = goog.array.slice(arguments, 0);
        449
        450 var message = args.length > 2 ? args.shift() : '';
        451 if (message) message += '\n';
        452
        453 var actualValue = args.shift();
        454 var matcher = args.shift();
        455
        456 return webdriver.promise.when(actualValue, function(value) {
        457 goog.labs.testing.assertThat(value, matcher, message);
        458 });
        459};
        460
        461
        462/**
        463 * Creates an equality matcher.
        464 * @param {*} expected The expected value.
        465 * @return {!goog.labs.testing.Matcher} The new matcher.
        466 */
        467webdriver.testing.asserts.equalTo = function(expected) {
        468 if (goog.isString(expected)) {
        469 return new goog.labs.testing.EqualsMatcher(expected);
        470 } else if (goog.isNumber(expected)) {
        471 return new goog.labs.testing.EqualToMatcher(expected);
        472 } else {
        473 return new goog.labs.testing.ObjectEqualsMatcher(
        474 /** @type {!Object} */ (expected));
        475 }
        476};
        477
        478
        479goog.exportSymbol('assertThat', webdriver.testing.asserts.assertThat);
        480// Mappings for goog.labs.testing matcher functions to the legacy
        481// webdriver.testing.asserts matchers.
        482goog.exportSymbol('contains', containsString);
        483goog.exportSymbol('equalTo', webdriver.testing.asserts.equalTo);
        484goog.exportSymbol('equals', webdriver.testing.asserts.equalTo);
        485goog.exportSymbol('is', webdriver.testing.asserts.equalTo);
        486goog.exportSymbol('not', isNot);
        487goog.exportSymbol('or', anyOf);
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/testing/testcase.js.src.html b/docs/source/lib/webdriver/testing/testcase.js.src.html new file mode 100644 index 0000000..334dcf7 --- /dev/null +++ b/docs/source/lib/webdriver/testing/testcase.js.src.html @@ -0,0 +1 @@ +testcase.js

        lib/webdriver/testing/testcase.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 special test case that runs each test inside of a
        17 * {@code webdriver.Application}. This allows each phase to schedule
        18 * asynchronous actions that run to completion before the next phase of the
        19 * test.
        20 *
        21 * This file requires the global {@code G_testRunner} to be initialized before
        22 * use. This can be accomplished by also importing
        23 * {@link webdriver.testing.jsunit}. This namespace is not required by default
        24 * to improve interoperability with other namespaces that may initialize
        25 * G_testRunner.
        26 */
        27
        28goog.provide('webdriver.testing.TestCase');
        29
        30goog.require('goog.testing.TestCase');
        31goog.require('webdriver.promise.ControlFlow');
        32/** @suppress {extraRequire} Imported for user convenience. */
        33goog.require('webdriver.testing.asserts');
        34
        35
        36
        37/**
        38 * Constructs a test case that synchronizes each test case with the singleton
        39 * {@code webdriver.promise.ControlFlow}.
        40 *
        41 * @param {!webdriver.testing.Client} client The test client to use for
        42 * reporting test results.
        43 * @param {string=} opt_name The name of the test case, defaults to
        44 * 'Untitled Test Case'.
        45 * @constructor
        46 * @extends {goog.testing.TestCase}
        47 */
        48webdriver.testing.TestCase = function(client, opt_name) {
        49 goog.base(this, opt_name);
        50
        51 /** @private {!webdriver.testing.Client} */
        52 this.client_ = client;
        53};
        54goog.inherits(webdriver.testing.TestCase, goog.testing.TestCase);
        55
        56
        57/**
        58 * Executes the next test inside its own {@code webdriver.Application}.
        59 * @override
        60 */
        61webdriver.testing.TestCase.prototype.cycleTests = function() {
        62 var test = this.next();
        63 if (!test) {
        64 this.finalize();
        65 return;
        66 }
        67
        68 goog.testing.TestCase.currentTestName = test.name;
        69 this.result_.runCount++;
        70 this.log('Running test: ' + test.name);
        71 this.client_.sendTestStartedEvent(test.name);
        72
        73 var self = this;
        74 var hadError = false;
        75 var app = webdriver.promise.controlFlow();
        76
        77 this.runSingleTest_(test, onError).then(function() {
        78 hadError || self.doSuccess(test);
        79 self.timeout(function() {
        80 self.cycleTests();
        81 }, 100);
        82 });
        83
        84 function onError(e) {
        85 hadError = true;
        86 self.doError(test, app.annotateError(e));
        87 // Note: result_ is a @protected field but still uses the trailing
        88 // underscore.
        89 var err = self.result_.errors[self.result_.errors.length - 1];
        90 self.client_.sendErrorEvent(err.toString());
        91 }
        92};
        93
        94
        95/** @override */
        96webdriver.testing.TestCase.prototype.logError = function(name, opt_e) {
        97 var errMsg = null;
        98 var stack = null;
        99 if (opt_e) {
        100 this.log(opt_e);
        101 if (goog.isString(opt_e)) {
        102 errMsg = opt_e;
        103 } else {
        104 // In case someone calls this function directly, make sure we have a
        105 // properly annotated error.
        106 webdriver.promise.controlFlow().annotateError(opt_e);
        107 errMsg = opt_e.toString();
        108 stack = opt_e.stack.substring(errMsg.length + 1);
        109 }
        110 } else {
        111 errMsg = 'An unknown error occurred';
        112 }
        113 var err = new goog.testing.TestCase.Error(name, errMsg, stack);
        114
        115 // Avoid double logging.
        116 if (!opt_e || !opt_e['isJsUnitException'] ||
        117 !opt_e['loggedJsUnitException']) {
        118 this.saveMessage(err.toString());
        119 }
        120
        121 if (opt_e && opt_e['isJsUnitException']) {
        122 opt_e['loggedJsUnitException'] = true;
        123 }
        124
        125 return err;
        126};
        127
        128
        129/**
        130 * Executes a single test, scheduling each phase with the global application.
        131 * Each phase will wait for the application to go idle before moving on to the
        132 * next test phase. This function models the follow basic test flow:
        133 *
        134 * try {
        135 * this.setUp.call(test.scope);
        136 * test.ref.call(test.scope);
        137 * } catch (ex) {
        138 * onError(ex);
        139 * } finally {
        140 * try {
        141 * this.tearDown.call(test.scope);
        142 * } catch (e) {
        143 * onError(e);
        144 * }
        145 * }
        146 *
        147 * @param {!goog.testing.TestCase.Test} test The test to run.
        148 * @param {function(*)} onError The function to call each time an error is
        149 * detected.
        150 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        151 * test has finished running.
        152 * @private
        153 */
        154webdriver.testing.TestCase.prototype.runSingleTest_ = function(test, onError) {
        155 var flow = webdriver.promise.controlFlow();
        156 flow.clearHistory();
        157
        158 return execute(test.name + '.setUp()', this.setUp)().
        159 then(execute(test.name + '()', test.ref)).
        160 thenCatch(onError).
        161 then(execute(test.name + '.tearDown()', this.tearDown)).
        162 thenCatch(onError);
        163
        164 function execute(description, fn) {
        165 return function() {
        166 return flow.execute(goog.bind(fn, test.scope), description);
        167 }
        168 }
        169};
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/touchsequence.js.src.html b/docs/source/lib/webdriver/touchsequence.js.src.html new file mode 100644 index 0000000..21fdb0f --- /dev/null +++ b/docs/source/lib/webdriver/touchsequence.js.src.html @@ -0,0 +1 @@ +touchsequence.js

        lib/webdriver/touchsequence.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18goog.provide('webdriver.TouchSequence');
        19
        20goog.require('goog.array');
        21goog.require('webdriver.Command');
        22goog.require('webdriver.CommandName');
        23
        24
        25
        26/**
        27 * Class for defining sequences of user touch interactions. Each sequence
        28 * will not be executed until {@link #perform} is called.
        29 *
        30 * Example:
        31 *
        32 * new webdriver.TouchSequence(driver).
        33 * tapAndHold({x: 0, y: 0}).
        34 * move({x: 3, y: 4}).
        35 * release({x: 10, y: 10}).
        36 * perform();
        37 *
        38 * @param {!webdriver.WebDriver} driver The driver instance to use.
        39 * @constructor
        40 */
        41webdriver.TouchSequence = function(driver) {
        42
        43 /** @private {!webdriver.WebDriver} */
        44 this.driver_ = driver;
        45
        46 /** @private {!Array<{description: string, command: !webdriver.Command}>} */
        47 this.touchActions_ = [];
        48};
        49
        50
        51/**
        52 * Schedules an action to be executed each time {@link #perform} is called on
        53 * this instance.
        54 * @param {string} description A description of the command.
        55 * @param {!webdriver.Command} command The command.
        56 * @private
        57 */
        58webdriver.TouchSequence.prototype.schedule_ = function(description, command) {
        59 this.touchActions_.push({
        60 description: description,
        61 command: command
        62 });
        63};
        64
        65
        66/**
        67 * Executes this action sequence.
        68 * @return {!webdriver.promise.Promise} A promise that will be resolved once
        69 * this sequence has completed.
        70 */
        71webdriver.TouchSequence.prototype.perform = function() {
        72 // Make a protected copy of the scheduled actions. This will protect against
        73 // users defining additional commands before this sequence is actually
        74 // executed.
        75 var actions = goog.array.clone(this.touchActions_);
        76 var driver = this.driver_;
        77 return driver.controlFlow().execute(function() {
        78 goog.array.forEach(actions, function(action) {
        79 driver.schedule(action.command, action.description);
        80 });
        81 }, 'TouchSequence.perform');
        82};
        83
        84
        85/**
        86 * Taps an element.
        87 *
        88 * @param {!webdriver.WebElement} elem The element to tap.
        89 * @return {!webdriver.TouchSequence} A self reference.
        90 */
        91webdriver.TouchSequence.prototype.tap = function(elem) {
        92 var command = new webdriver.Command(webdriver.CommandName.TOUCH_SINGLE_TAP).
        93 setParameter('element', elem.getRawId());
        94
        95 this.schedule_('tap', command);
        96 return this;
        97};
        98
        99
        100/**
        101 * Double taps an element.
        102 *
        103 * @param {!webdriver.WebElement} elem The element to double tap.
        104 * @return {!webdriver.TouchSequence} A self reference.
        105 */
        106webdriver.TouchSequence.prototype.doubleTap = function(elem) {
        107 var command = new webdriver.Command(webdriver.CommandName.TOUCH_DOUBLE_TAP).
        108 setParameter('element', elem.getRawId());
        109
        110 this.schedule_('doubleTap', command);
        111 return this;
        112};
        113
        114
        115/**
        116 * Long press on an element.
        117 *
        118 * @param {!webdriver.WebElement} elem The element to long press.
        119 * @return {!webdriver.TouchSequence} A self reference.
        120 */
        121webdriver.TouchSequence.prototype.longPress = function(elem) {
        122 var command = new webdriver.Command(webdriver.CommandName.TOUCH_LONG_PRESS).
        123 setParameter('element', elem.getRawId());
        124
        125 this.schedule_('longPress', command);
        126 return this;
        127};
        128
        129
        130/**
        131 * Touch down at the given location.
        132 *
        133 * @param {{x: number, y: number}} location The location to touch down at.
        134 * @return {!webdriver.TouchSequence} A self reference.
        135 */
        136webdriver.TouchSequence.prototype.tapAndHold = function(location) {
        137 var command = new webdriver.Command(webdriver.CommandName.TOUCH_DOWN).
        138 setParameter('x', location.x).
        139 setParameter('y', location.y);
        140
        141 this.schedule_('tapAndHold', command);
        142 return this;
        143};
        144
        145
        146/**
        147 * Move a held {@linkplain #tapAndHold touch} to the specified location.
        148 *
        149 * @param {{x: number, y: number}} location The location to move to.
        150 * @return {!webdriver.TouchSequence} A self reference.
        151 */
        152webdriver.TouchSequence.prototype.move = function(location) {
        153 var command = new webdriver.Command(webdriver.CommandName.TOUCH_MOVE).
        154 setParameter('x', location.x).
        155 setParameter('y', location.y);
        156
        157 this.schedule_('move', command);
        158 return this;
        159};
        160
        161
        162/**
        163 * Release a held {@linkplain #tapAndHold touch} at the specified location.
        164 *
        165 * @param {{x: number, y: number}} location The location to release at.
        166 * @return {!webdriver.TouchSequence} A self reference.
        167 */
        168webdriver.TouchSequence.prototype.release = function(location) {
        169 var command = new webdriver.Command(webdriver.CommandName.TOUCH_UP).
        170 setParameter('x', location.x).
        171 setParameter('y', location.y);
        172
        173 this.schedule_('release', command);
        174 return this;
        175};
        176
        177
        178/**
        179 * Scrolls the touch screen by the given offset.
        180 *
        181 * @param {{x: number, y: number}} offset The offset to scroll to.
        182 * @return {!webdriver.TouchSequence} A self reference.
        183 */
        184webdriver.TouchSequence.prototype.scroll = function(offset) {
        185 var command = new webdriver.Command(webdriver.CommandName.TOUCH_SCROLL).
        186 setParameter('xoffset', offset.x).
        187 setParameter('yoffset', offset.y);
        188
        189 this.schedule_('scroll', command);
        190 return this;
        191};
        192
        193
        194/**
        195 * Scrolls the touch screen, starting on `elem` and moving by the specified
        196 * offset.
        197 *
        198 * @param {!webdriver.WebElement} elem The element where scroll starts.
        199 * @param {{x: number, y: number}} offset The offset to scroll to.
        200 * @return {!webdriver.TouchSequence} A self reference.
        201 */
        202webdriver.TouchSequence.prototype.scrollFromElement = function(elem, offset) {
        203 var command = new webdriver.Command(webdriver.CommandName.TOUCH_SCROLL).
        204 setParameter('element', elem.getRawId()).
        205 setParameter('xoffset', offset.x).
        206 setParameter('yoffset', offset.y);
        207
        208 this.schedule_('scrollFromElement', command);
        209 return this;
        210};
        211
        212
        213/**
        214 * Flick, starting anywhere on the screen, at speed xspeed and yspeed.
        215 *
        216 * @param {{xspeed: number, yspeed: number}} speed The speed to flick in each
        217 direction, in pixels per second.
        218 * @return {!webdriver.TouchSequence} A self reference.
        219 */
        220webdriver.TouchSequence.prototype.flick = function(speed) {
        221 var command = new webdriver.Command(webdriver.CommandName.TOUCH_FLICK).
        222 setParameter('xspeed', speed.xspeed).
        223 setParameter('yspeed', speed.yspeed);
        224
        225 this.schedule_('flick', command);
        226 return this;
        227};
        228
        229
        230/**
        231 * Flick starting at elem and moving by x and y at specified speed.
        232 *
        233 * @param {!webdriver.WebElement} elem The element where flick starts.
        234 * @param {{x: number, y: number}} offset The offset to flick to.
        235 * @param {number} speed The speed to flick at in pixels per second.
        236 * @return {!webdriver.TouchSequence} A self reference.
        237 */
        238webdriver.TouchSequence.prototype.flickElement = function(elem, offset, speed) {
        239 var command = new webdriver.Command(webdriver.CommandName.TOUCH_FLICK).
        240 setParameter('element', elem.getRawId()).
        241 setParameter('xoffset', offset.x).
        242 setParameter('yoffset', offset.y).
        243 setParameter('speed', speed);
        244
        245 this.schedule_('flickElement', command);
        246 return this;
        247};
        248
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/until.js.src.html b/docs/source/lib/webdriver/until.js.src.html new file mode 100644 index 0000000..bfe9ce0 --- /dev/null +++ b/docs/source/lib/webdriver/until.js.src.html @@ -0,0 +1 @@ +until.js

        lib/webdriver/until.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines common conditions for use with
        20 * {@link webdriver.WebDriver#wait WebDriver wait}.
        21 *
        22 * Sample usage:
        23 *
        24 * driver.get('http://www.google.com/ncr');
        25 *
        26 * var query = driver.wait(until.elementLocated(By.name('q')));
        27 * query.sendKeys('webdriver\n');
        28 *
        29 * driver.wait(until.titleIs('webdriver - Google Search'));
        30 *
        31 * To define a custom condition, simply call WebDriver.wait with a function
        32 * that will eventually return a truthy-value (neither null, undefined, false,
        33 * 0, or the empty string):
        34 *
        35 * driver.wait(function() {
        36 * return driver.getTitle().then(function(title) {
        37 * return title === 'webdriver - Google Search';
        38 * });
        39 * }, 1000);
        40 */
        41
        42goog.provide('webdriver.until');
        43
        44goog.require('bot.ErrorCode');
        45goog.require('goog.array');
        46goog.require('goog.string');
        47goog.require('webdriver.Locator');
        48
        49
        50
        51goog.scope(function() {
        52
        53var until = webdriver.until;
        54
        55
        56/**
        57 * Defines a condition to
        58 * @param {string} message A descriptive error message. Should complete the
        59 * sentence "Waiting [...]"
        60 * @param {function(!webdriver.WebDriver): OUT} fn The condition function to
        61 * evaluate on each iteration of the wait loop.
        62 * @constructor
        63 * @struct
        64 * @final
        65 * @template OUT
        66 */
        67until.Condition = function(message, fn) {
        68 /** @private {string} */
        69 this.description_ = 'Waiting ' + message;
        70
        71 /** @type {function(!webdriver.WebDriver): OUT} */
        72 this.fn = fn;
        73};
        74
        75
        76/** @return {string} A description of this condition. */
        77until.Condition.prototype.description = function() {
        78 return this.description_;
        79};
        80
        81
        82/**
        83 * Creates a condition that will wait until the input driver is able to switch
        84 * to the designated frame. The target frame may be specified as
        85 *
        86 * 1. a numeric index into
        87 * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames)
        88 * for the currently selected frame.
        89 * 2. a {@link webdriver.WebElement}, which must reference a FRAME or IFRAME
        90 * element on the current page.
        91 * 3. a locator which may be used to first locate a FRAME or IFRAME on the
        92 * current page before attempting to switch to it.
        93 *
        94 * Upon successful resolution of this condition, the driver will be left
        95 * focused on the new frame.
        96 *
        97 * @param {!(number|webdriver.WebElement|
        98 * webdriver.Locator|webdriver.By.Hash|
        99 * function(!webdriver.WebDriver): !webdriver.WebElement)} frame
        100 * The frame identifier.
        101 * @return {!until.Condition.<boolean>} A new condition.
        102 */
        103until.ableToSwitchToFrame = function(frame) {
        104 var condition;
        105 if (goog.isNumber(frame) || frame instanceof webdriver.WebElement) {
        106 condition = attemptToSwitchFrames;
        107 } else {
        108 condition = function(driver) {
        109 var locator =
        110 /** @type {!(webdriver.Locator|webdriver.By.Hash|Function)} */(frame);
        111 return driver.findElements(locator).then(function(els) {
        112 if (els.length) {
        113 return attemptToSwitchFrames(driver, els[0]);
        114 }
        115 });
        116 };
        117 }
        118
        119 return new until.Condition('to be able to switch to frame', condition);
        120
        121 function attemptToSwitchFrames(driver, frame) {
        122 return driver.switchTo().frame(frame).then(
        123 function() { return true; },
        124 function(e) {
        125 if (e && e.code !== bot.ErrorCode.NO_SUCH_FRAME) {
        126 throw e;
        127 }
        128 });
        129 }
        130};
        131
        132
        133/**
        134 * Creates a condition that waits for an alert to be opened. Upon success, the
        135 * returned promise will be fulfilled with the handle for the opened alert.
        136 *
        137 * @return {!until.Condition.<!webdriver.Alert>} The new condition.
        138 */
        139until.alertIsPresent = function() {
        140 return new until.Condition('for alert to be present', function(driver) {
        141 return driver.switchTo().alert().thenCatch(function(e) {
        142 if (e && e.code !== bot.ErrorCode.NO_SUCH_ALERT) {
        143 throw e;
        144 }
        145 });
        146 });
        147};
        148
        149
        150/**
        151 * Creates a condition that will wait for the current page's title to match the
        152 * given value.
        153 *
        154 * @param {string} title The expected page title.
        155 * @return {!until.Condition.<boolean>} The new condition.
        156 */
        157until.titleIs = function(title) {
        158 return new until.Condition(
        159 'for title to be ' + goog.string.quote(title),
        160 function(driver) {
        161 return driver.getTitle().then(function(t) {
        162 return t === title;
        163 });
        164 });
        165};
        166
        167
        168/**
        169 * Creates a condition that will wait for the current page's title to contain
        170 * the given substring.
        171 *
        172 * @param {string} substr The substring that should be present in the page
        173 * title.
        174 * @return {!until.Condition.<boolean>} The new condition.
        175 */
        176until.titleContains = function(substr) {
        177 return new until.Condition(
        178 'for title to contain ' + goog.string.quote(substr),
        179 function(driver) {
        180 return driver.getTitle().then(function(title) {
        181 return title.indexOf(substr) !== -1;
        182 });
        183 });
        184};
        185
        186
        187/**
        188 * Creates a condition that will wait for the current page's title to match the
        189 * given regular expression.
        190 *
        191 * @param {!RegExp} regex The regular expression to test against.
        192 * @return {!until.Condition.<boolean>} The new condition.
        193 */
        194until.titleMatches = function(regex) {
        195 return new until.Condition('for title to match ' + regex, function(driver) {
        196 return driver.getTitle().then(function(title) {
        197 return regex.test(title);
        198 });
        199 });
        200};
        201
        202
        203/**
        204 * Creates a condition that will loop until an element is
        205 * {@link webdriver.WebDriver#findElement found} with the given locator.
        206 *
        207 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator
        208 * to use.
        209 * @return {!until.Condition.<!webdriver.WebElement>} The new condition.
        210 */
        211until.elementLocated = function(locator) {
        212 locator = webdriver.Locator.checkLocator(locator);
        213 var locatorStr = goog.isFunction(locator) ? 'by function()' : locator + '';
        214 return new until.Condition('for element to be located ' + locatorStr,
        215 function(driver) {
        216 return driver.findElements(locator).then(function(elements) {
        217 return elements[0];
        218 });
        219 });
        220};
        221
        222
        223/**
        224 * Creates a condition that will loop until at least one element is
        225 * {@link webdriver.WebDriver#findElement found} with the given locator.
        226 *
        227 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator
        228 * to use.
        229 * @return {!until.Condition.<!Array.<!webdriver.WebElement>>} The new
        230 * condition.
        231 */
        232until.elementsLocated = function(locator) {
        233 locator = webdriver.Locator.checkLocator(locator);
        234 var locatorStr = goog.isFunction(locator) ? 'by function()' : locator + '';
        235 return new until.Condition(
        236 'for at least one element to be located ' + locatorStr,
        237 function(driver) {
        238 return driver.findElements(locator).then(function(elements) {
        239 return elements.length > 0 ? elements : null;
        240 });
        241 });
        242};
        243
        244
        245/**
        246 * Creates a condition that will wait for the given element to become stale. An
        247 * element is considered stale once it is removed from the DOM, or a new page
        248 * has loaded.
        249 *
        250 * @param {!webdriver.WebElement} element The element that should become stale.
        251 * @return {!until.Condition.<boolean>} The new condition.
        252 */
        253until.stalenessOf = function(element) {
        254 return new until.Condition('element to become stale', function() {
        255 return element.getTagName().then(
        256 function() { return false; },
        257 function(e) {
        258 if (e.code === bot.ErrorCode.STALE_ELEMENT_REFERENCE) {
        259 return true;
        260 }
        261 throw e;
        262 });
        263 });
        264};
        265
        266
        267/**
        268 * Creates a condition that will wait for the given element to become visible.
        269 *
        270 * @param {!webdriver.WebElement} element The element to test.
        271 * @return {!until.Condition.<boolean>} The new condition.
        272 * @see webdriver.WebDriver#isDisplayed
        273 */
        274until.elementIsVisible = function(element) {
        275 return new until.Condition('until element is visible', function() {
        276 return element.isDisplayed();
        277 });
        278};
        279
        280
        281/**
        282 * Creates a condition that will wait for the given element to be in the DOM,
        283 * yet not visible to the user.
        284 *
        285 * @param {!webdriver.WebElement} element The element to test.
        286 * @return {!until.Condition.<boolean>} The new condition.
        287 * @see webdriver.WebDriver#isDisplayed
        288 */
        289until.elementIsNotVisible = function(element) {
        290 return new until.Condition('until element is not visible', function() {
        291 return element.isDisplayed().then(function(v) {
        292 return !v;
        293 });
        294 });
        295};
        296
        297
        298/**
        299 * Creates a condition that will wait for the given element to be enabled.
        300 *
        301 * @param {!webdriver.WebElement} element The element to test.
        302 * @return {!until.Condition.<boolean>} The new condition.
        303 * @see webdriver.WebDriver#isEnabled
        304 */
        305until.elementIsEnabled = function(element) {
        306 return new until.Condition('until element is enabled', function() {
        307 return element.isEnabled();
        308 });
        309};
        310
        311
        312/**
        313 * Creates a condition that will wait for the given element to be disabled.
        314 *
        315 * @param {!webdriver.WebElement} element The element to test.
        316 * @return {!until.Condition.<boolean>} The new condition.
        317 * @see webdriver.WebDriver#isEnabled
        318 */
        319until.elementIsDisabled = function(element) {
        320 return new until.Condition('until element is disabled', function() {
        321 return element.isEnabled().then(function(v) {
        322 return !v;
        323 });
        324 });
        325};
        326
        327
        328/**
        329 * Creates a condition that will wait for the given element to be selected.
        330 * @param {!webdriver.WebElement} element The element to test.
        331 * @return {!until.Condition.<boolean>} The new condition.
        332 * @see webdriver.WebDriver#isSelected
        333 */
        334until.elementIsSelected = function(element) {
        335 return new until.Condition('until element is selected', function() {
        336 return element.isSelected();
        337 });
        338};
        339
        340
        341/**
        342 * Creates a condition that will wait for the given element to be deselected.
        343 *
        344 * @param {!webdriver.WebElement} element The element to test.
        345 * @return {!until.Condition.<boolean>} The new condition.
        346 * @see webdriver.WebDriver#isSelected
        347 */
        348until.elementIsNotSelected = function(element) {
        349 return new until.Condition('until element is not selected', function() {
        350 return element.isSelected().then(function(v) {
        351 return !v;
        352 });
        353 });
        354};
        355
        356
        357/**
        358 * Creates a condition that will wait for the given element's
        359 * {@link webdriver.WebDriver#getText visible text} to match the given
        360 * {@code text} exactly.
        361 *
        362 * @param {!webdriver.WebElement} element The element to test.
        363 * @param {string} text The expected text.
        364 * @return {!until.Condition.<boolean>} The new condition.
        365 * @see webdriver.WebDriver#getText
        366 */
        367until.elementTextIs = function(element, text) {
        368 return new until.Condition('until element text is', function() {
        369 return element.getText().then(function(t) {
        370 return t === text;
        371 });
        372 });
        373};
        374
        375
        376/**
        377 * Creates a condition that will wait for the given element's
        378 * {@link webdriver.WebDriver#getText visible text} to contain the given
        379 * substring.
        380 *
        381 * @param {!webdriver.WebElement} element The element to test.
        382 * @param {string} substr The substring to search for.
        383 * @return {!until.Condition.<boolean>} The new condition.
        384 * @see webdriver.WebDriver#getText
        385 */
        386until.elementTextContains = function(element, substr) {
        387 return new until.Condition('until element text contains', function() {
        388 return element.getText().then(function(t) {
        389 return t.indexOf(substr) != -1;
        390 });
        391 });
        392};
        393
        394
        395/**
        396 * Creates a condition that will wait for the given element's
        397 * {@link webdriver.WebDriver#getText visible text} to match a regular
        398 * expression.
        399 *
        400 * @param {!webdriver.WebElement} element The element to test.
        401 * @param {!RegExp} regex The regular expression to test against.
        402 * @return {!until.Condition.<boolean>} The new condition.
        403 * @see webdriver.WebDriver#getText
        404 */
        405until.elementTextMatches = function(element, regex) {
        406 return new until.Condition('until element text matches', function() {
        407 return element.getText().then(function(t) {
        408 return regex.test(t);
        409 });
        410 });
        411};
        412}); // goog.scope
        \ No newline at end of file diff --git a/docs/source/lib/webdriver/webdriver.js.src.html b/docs/source/lib/webdriver/webdriver.js.src.html index f65ebea..8a5ecc8 100644 --- a/docs/source/lib/webdriver/webdriver.js.src.html +++ b/docs/source/lib/webdriver/webdriver.js.src.html @@ -1 +1 @@ -webdriver.js

        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
        19goog.provide('webdriver.Alert');
        20goog.provide('webdriver.UnhandledAlertError');
        21goog.provide('webdriver.WebDriver');
        22goog.provide('webdriver.WebElement');
        23
        24goog.require('bot.Error');
        25goog.require('bot.ErrorCode');
        26goog.require('bot.response');
        27goog.require('goog.array');
        28goog.require('goog.object');
        29goog.require('webdriver.ActionSequence');
        30goog.require('webdriver.Command');
        31goog.require('webdriver.CommandName');
        32goog.require('webdriver.Key');
        33goog.require('webdriver.Locator');
        34goog.require('webdriver.Session');
        35goog.require('webdriver.logging');
        36goog.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 */
        74webdriver.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 */
        94webdriver.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 */
        110webdriver.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 */
        130webdriver.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 */
        162webdriver.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 */
        206webdriver.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 */
        232webdriver.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 */
        247webdriver.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} A promise that will be resolved with
        258 * the command result.
        259 */
        260webdriver.WebDriver.prototype.schedule = function(command, description) {
        261 var self = this;
        262
        263 checkHasNotQuit();
        264 command.setParameter('sessionId', this.session_);
        265
        266 var flow = this.flow_;
        267 return flow.execute(function() {
        268 // A call to WebDriver.quit() may have been scheduled in the same event
        269 // loop as this |command|, which would prevent us from detecting that the
        270 // driver has quit above. Therefore, we need to make another quick check.
        271 // We still check above so we can fail as early as possible.
        272 checkHasNotQuit();
        273 return webdriver.WebDriver.executeCommand_(self.executor_, command);
        274 }, description).then(function(response) {
        275 try {
        276 bot.response.checkResponse(response);
        277 } catch (ex) {
        278 var value = response['value'];
        279 if (ex.code === bot.ErrorCode.MODAL_DIALOG_OPENED) {
        280 var text = value && value['alert'] ? value['alert']['text'] : '';
        281 throw new webdriver.UnhandledAlertError(ex.message,
        282 new webdriver.Alert(self, text));
        283 }
        284 throw ex;
        285 }
        286 return webdriver.WebDriver.fromWireValue_(self, response['value']);
        287 });
        288
        289 function checkHasNotQuit() {
        290 if (!self.session_) {
        291 throw new Error('This driver instance does not have a valid session ID ' +
        292 '(did you call WebDriver.quit()?) and may no longer be ' +
        293 'used.');
        294 }
        295 }
        296};
        297
        298
        299// ----------------------------------------------------------------------------
        300// Client command functions:
        301// ----------------------------------------------------------------------------
        302
        303
        304/**
        305 * @return {!webdriver.promise.Promise} A promise for this client's session.
        306 */
        307webdriver.WebDriver.prototype.getSession = function() {
        308 return webdriver.promise.when(this.session_);
        309};
        310
        311
        312/**
        313 * @return {!webdriver.promise.Promise} A promise that will resolve with the
        314 * this instance's capabilities.
        315 */
        316webdriver.WebDriver.prototype.getCapabilities = function() {
        317 return webdriver.promise.when(this.session_, function(session) {
        318 return session.getCapabilities();
        319 });
        320};
        321
        322
        323/**
        324 * Schedules a command to quit the current session. After calling quit, this
        325 * instance will be invalidated and may no longer be used to issue commands
        326 * against the browser.
        327 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        328 * the command has completed.
        329 */
        330webdriver.WebDriver.prototype.quit = function() {
        331 var result = this.schedule(
        332 new webdriver.Command(webdriver.CommandName.QUIT),
        333 'WebDriver.quit()');
        334 // Delete our session ID when the quit command finishes; this will allow us to
        335 // throw an error when attemnpting to use a driver post-quit.
        336 return result.thenFinally(goog.bind(function() {
        337 delete this.session_;
        338 }, this));
        339};
        340
        341
        342/**
        343 * Creates a new action sequence using this driver. The sequence will not be
        344 * scheduled for execution until {@link webdriver.ActionSequence#perform} is
        345 * called. Example:
        346 * <pre><code>
        347 * driver.actions().
        348 * mouseDown(element1).
        349 * mouseMove(element2).
        350 * mouseUp().
        351 * perform();
        352 * </code></pre>
        353 * @return {!webdriver.ActionSequence} A new action sequence for this instance.
        354 */
        355webdriver.WebDriver.prototype.actions = function() {
        356 return new webdriver.ActionSequence(this);
        357};
        358
        359
        360/**
        361 * Schedules a command to execute JavaScript in the context of the currently
        362 * selected frame or window. The script fragment will be executed as the body
        363 * of an anonymous function. If the script is provided as a function object,
        364 * that function will be converted to a string for injection into the target
        365 * window.
        366 *
        367 * Any arguments provided in addition to the script will be included as script
        368 * arguments and may be referenced using the {@code arguments} object.
        369 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}.
        370 * Arrays and objects may also be used as script arguments as long as each item
        371 * adheres to the types previously mentioned.
        372 *
        373 * The script may refer to any variables accessible from the current window.
        374 * Furthermore, the script will execute in the window's context, thus
        375 * {@code document} may be used to refer to the current document. Any local
        376 * variables will not be available once the script has finished executing,
        377 * though global variables will persist.
        378 *
        379 * If the script has a return value (i.e. if the script contains a return
        380 * statement), then the following steps will be taken for resolving this
        381 * functions return value:
        382 * <ul>
        383 * <li>For a HTML element, the value will resolve to a
        384 * {@code webdriver.WebElement}</li>
        385 * <li>Null and undefined return values will resolve to null</li>
        386 * <li>Booleans, numbers, and strings will resolve as is</li>
        387 * <li>Functions will resolve to their string representation</li>
        388 * <li>For arrays and objects, each member item will be converted according to
        389 * the rules above</li>
        390 * </ul>
        391 *
        392 * @param {!(string|Function)} script The script to execute.
        393 * @param {...*} var_args The arguments to pass to the script.
        394 * @return {!webdriver.promise.Promise} A promise that will resolve to the
        395 * scripts return value.
        396 */
        397webdriver.WebDriver.prototype.executeScript = function(script, var_args) {
        398 if (goog.isFunction(script)) {
        399 script = 'return (' + script + ').apply(null, arguments);';
        400 }
        401 return this.schedule(
        402 new webdriver.Command(webdriver.CommandName.EXECUTE_SCRIPT).
        403 setParameter('script', script).
        404 setParameter('args', goog.array.slice(arguments, 1)),
        405 'WebDriver.executeScript()');
        406};
        407
        408
        409/**
        410 * Schedules a command to execute asynchronous JavaScript in the context of the
        411 * currently selected frame or window. The script fragment will be executed as
        412 * the body of an anonymous function. If the script is provided as a function
        413 * object, that function will be converted to a string for injection into the
        414 * target window.
        415 *
        416 * Any arguments provided in addition to the script will be included as script
        417 * arguments and may be referenced using the {@code arguments} object.
        418 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}.
        419 * Arrays and objects may also be used as script arguments as long as each item
        420 * adheres to the types previously mentioned.
        421 *
        422 * Unlike executing synchronous JavaScript with
        423 * {@code webdriver.WebDriver.prototype.executeScript}, scripts executed with
        424 * this function must explicitly signal they are finished by invoking the
        425 * provided callback. This callback will always be injected into the
        426 * executed function as the last argument, and thus may be referenced with
        427 * {@code arguments[arguments.length - 1]}. The following steps will be taken
        428 * for resolving this functions return value against the first argument to the
        429 * script's callback function:
        430 * <ul>
        431 * <li>For a HTML element, the value will resolve to a
        432 * {@code webdriver.WebElement}</li>
        433 * <li>Null and undefined return values will resolve to null</li>
        434 * <li>Booleans, numbers, and strings will resolve as is</li>
        435 * <li>Functions will resolve to their string representation</li>
        436 * <li>For arrays and objects, each member item will be converted according to
        437 * the rules above</li>
        438 * </ul>
        439 *
        440 * Example #1: Performing a sleep that is synchronized with the currently
        441 * selected window:
        442 * <code><pre>
        443 * var start = new Date().getTime();
        444 * driver.executeAsyncScript(
        445 * 'window.setTimeout(arguments[arguments.length - 1], 500);').
        446 * then(function() {
        447 * console.log('Elapsed time: ' + (new Date().getTime() - start) + ' ms');
        448 * });
        449 * </pre></code>
        450 *
        451 * Example #2: Synchronizing a test with an AJAX application:
        452 * <code><pre>
        453 * var button = driver.findElement(By.id('compose-button'));
        454 * button.click();
        455 * driver.executeAsyncScript(
        456 * 'var callback = arguments[arguments.length - 1];' +
        457 * 'mailClient.getComposeWindowWidget().onload(callback);');
        458 * driver.switchTo().frame('composeWidget');
        459 * driver.findElement(By.id('to')).sendKEys('dog@example.com');
        460 * </pre></code>
        461 *
        462 * Example #3: Injecting a XMLHttpRequest and waiting for the result. In this
        463 * example, the inject script is specified with a function literal. When using
        464 * this format, the function is converted to a string for injection, so it
        465 * should not reference any symbols not defined in the scope of the page under
        466 * test.
        467 * <code><pre>
        468 * driver.executeAsyncScript(function() {
        469 * var callback = arguments[arguments.length - 1];
        470 * var xhr = new XMLHttpRequest();
        471 * xhr.open("GET", "/resource/data.json", true);
        472 * xhr.onreadystatechange = function() {
        473 * if (xhr.readyState == 4) {
        474 * callback(xhr.resposneText);
        475 * }
        476 * }
        477 * xhr.send('');
        478 * }).then(function(str) {
        479 * console.log(JSON.parse(str)['food']);
        480 * });
        481 * </pre></code>
        482 *
        483 * @param {!(string|Function)} script The script to execute.
        484 * @param {...*} var_args The arguments to pass to the script.
        485 * @return {!webdriver.promise.Promise} A promise that will resolve to the
        486 * scripts return value.
        487 */
        488webdriver.WebDriver.prototype.executeAsyncScript = function(script, var_args) {
        489 if (goog.isFunction(script)) {
        490 script = 'return (' + script + ').apply(null, arguments);';
        491 }
        492 return this.schedule(
        493 new webdriver.Command(webdriver.CommandName.EXECUTE_ASYNC_SCRIPT).
        494 setParameter('script', script).
        495 setParameter('args', goog.array.slice(arguments, 1)),
        496 'WebDriver.executeScript()');
        497};
        498
        499
        500/**
        501 * Schedules a command to execute a custom function.
        502 * @param {!Function} fn The function to execute.
        503 * @param {Object=} opt_scope The object in whose scope to execute the function.
        504 * @param {...*} var_args Any arguments to pass to the function.
        505 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        506 * function's result.
        507 */
        508webdriver.WebDriver.prototype.call = function(fn, opt_scope, var_args) {
        509 var args = goog.array.slice(arguments, 2);
        510 var flow = this.flow_;
        511 return flow.execute(function() {
        512 return webdriver.promise.fullyResolved(args).then(function(args) {
        513 return fn.apply(opt_scope, args);
        514 });
        515 }, 'WebDriver.call(' + (fn.name || 'function') + ')');
        516};
        517
        518
        519/**
        520 * Schedules a command to wait for a condition to hold, as defined by some
        521 * user supplied function. If any errors occur while evaluating the wait, they
        522 * will be allowed to propagate.
        523 * @param {function():boolean} fn The function to evaluate as a wait condition.
        524 * @param {number} timeout How long to wait for the condition to be true.
        525 * @param {string=} opt_message An optional message to use if the wait times
        526 * out.
        527 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        528 * wait condition has been satisfied.
        529 */
        530webdriver.WebDriver.prototype.wait = function(fn, timeout, opt_message) {
        531 return this.flow_.wait(fn, timeout, opt_message);
        532};
        533
        534
        535/**
        536 * Schedules a command to make the driver sleep for the given amount of time.
        537 * @param {number} ms The amount of time, in milliseconds, to sleep.
        538 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        539 * sleep has finished.
        540 */
        541webdriver.WebDriver.prototype.sleep = function(ms) {
        542 return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')');
        543};
        544
        545
        546/**
        547 * Schedules a command to retrieve they current window handle.
        548 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        549 * current window handle.
        550 */
        551webdriver.WebDriver.prototype.getWindowHandle = function() {
        552 return this.schedule(
        553 new webdriver.Command(webdriver.CommandName.GET_CURRENT_WINDOW_HANDLE),
        554 'WebDriver.getWindowHandle()');
        555};
        556
        557
        558/**
        559 * Schedules a command to retrieve the current list of available window handles.
        560 * @return {!webdriver.promise.Promise} A promise that will be resolved with an
        561 * array of window handles.
        562 */
        563webdriver.WebDriver.prototype.getAllWindowHandles = function() {
        564 return this.schedule(
        565 new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES),
        566 'WebDriver.getAllWindowHandles()');
        567};
        568
        569
        570/**
        571 * Schedules a command to retrieve the current page's source. The page source
        572 * returned is a representation of the underlying DOM: do not expect it to be
        573 * formatted or escaped in the same way as the response sent from the web
        574 * server.
        575 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        576 * current page source.
        577 */
        578webdriver.WebDriver.prototype.getPageSource = function() {
        579 return this.schedule(
        580 new webdriver.Command(webdriver.CommandName.GET_PAGE_SOURCE),
        581 'WebDriver.getAllWindowHandles()');
        582};
        583
        584
        585/**
        586 * Schedules a command to close the current window.
        587 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        588 * this command has completed.
        589 */
        590webdriver.WebDriver.prototype.close = function() {
        591 return this.schedule(new webdriver.Command(webdriver.CommandName.CLOSE),
        592 'WebDriver.close()');
        593};
        594
        595
        596/**
        597 * Schedules a command to navigate to the given URL.
        598 * @param {string} url The fully qualified URL to open.
        599 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        600 * document has finished loading.
        601 */
        602webdriver.WebDriver.prototype.get = function(url) {
        603 return this.navigate().to(url);
        604};
        605
        606
        607/**
        608 * Schedules a command to retrieve the URL of the current page.
        609 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        610 * current URL.
        611 */
        612webdriver.WebDriver.prototype.getCurrentUrl = function() {
        613 return this.schedule(
        614 new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL),
        615 'WebDriver.getCurrentUrl()');
        616};
        617
        618
        619/**
        620 * Schedules a command to retrieve the current page's title.
        621 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        622 * current page's title.
        623 */
        624webdriver.WebDriver.prototype.getTitle = function() {
        625 return this.schedule(new webdriver.Command(webdriver.CommandName.GET_TITLE),
        626 'WebDriver.getTitle()');
        627};
        628
        629
        630/**
        631 * Schedule a command to find an element on the page. If the element cannot be
        632 * found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned
        633 * by the driver. Unlike other commands, this error cannot be suppressed. In
        634 * other words, scheduling a command to find an element doubles as an assert
        635 * that the element is present on the page. To test whether an element is
        636 * present on the page, use {@link #isElementPresent} instead.
        637 *
        638 * <p>The search criteria for an element may be defined using one of the
        639 * factories in the {@link webdriver.By} namespace, or as a short-hand
        640 * {@link webdriver.By.Hash} object. For example, the following two statements
        641 * are equivalent:
        642 * <code><pre>
        643 * var e1 = driver.findElement(By.id('foo'));
        644 * var e2 = driver.findElement({id:'foo'});
        645 * </pre></code>
        646 *
        647 * <p>You may also provide a custom locator function, which takes as input
        648 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a
        649 * promise that will resolve to a WebElement. For example, to find the first
        650 * visible link on a page, you could write:
        651 * <code><pre>
        652 * var link = driver.findElement(firstVisibleLink);
        653 *
        654 * function firstVisibleLink(driver) {
        655 * var links = driver.findElements(By.tagName('a'));
        656 * return webdriver.promise.filter(links, function(link) {
        657 * return links.isDisplayed();
        658 * }).then(function(visibleLinks) {
        659 * return visibleLinks[0];
        660 * });
        661 * }
        662 * </pre></code>
        663 *
        664 * <p>When running in the browser, a WebDriver cannot manipulate DOM elements
        665 * directly; it may do so only through a {@link webdriver.WebElement} reference.
        666 * This function may be used to generate a WebElement from a DOM element. A
        667 * reference to the DOM element will be stored in a known location and this
        668 * driver will attempt to retrieve it through {@link #executeScript}. If the
        669 * element cannot be found (eg, it belongs to a different document than the
        670 * one this instance is currently focused on), a
        671 * {@link bot.ErrorCode.NO_SUCH_ELEMENT} error will be returned.
        672 *
        673 * @param {!(webdriver.Locator|webdriver.By.Hash|Element|Function)} locator The
        674 * locator to use.
        675 * @return {!webdriver.WebElement} A WebElement that can be used to issue
        676 * commands against the located element. If the element is not found, the
        677 * element will be invalidated and all scheduled commands aborted.
        678 */
        679webdriver.WebDriver.prototype.findElement = function(locator) {
        680 var id;
        681 if ('nodeType' in locator && 'ownerDocument' in locator) {
        682 var element = /** @type {!Element} */ (locator);
        683 id = this.findDomElement_(element).
        684 then(function(elements) {
        685 if (!elements.length) {
        686 throw new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT,
        687 'Unable to locate element. Is WebDriver focused on its ' +
        688 'ownerDocument\'s frame?');
        689 }
        690 return elements[0];
        691 });
        692 } else {
        693 locator = webdriver.Locator.checkLocator(locator);
        694 if (goog.isFunction(locator)) {
        695 id = this.findElementInternal_(locator, this);
        696 } else {
        697 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENT).
        698 setParameter('using', locator.using).
        699 setParameter('value', locator.value);
        700 id = this.schedule(command, 'WebDriver.findElement(' + locator + ')');
        701 }
        702 }
        703 return new webdriver.WebElement(this, id);
        704};
        705
        706
        707/**
        708 * @param {!Function} locatorFn The locator function to use.
        709 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search
        710 * context.
        711 * @return {!webdriver.promise.Promise.<!webdriver.WebElement>} A
        712 * promise that will resolve to a list of WebElements.
        713 * @private
        714 */
        715webdriver.WebDriver.prototype.findElementInternal_ = function(
        716 locatorFn, context) {
        717 return this.call(goog.partial(locatorFn, context)).then(function(result) {
        718 if (goog.isArray(result)) {
        719 result = result[0];
        720 }
        721 if (!(result instanceof webdriver.WebElement)) {
        722 throw new TypeError('Custom locator did not return a WebElement');
        723 }
        724 return result;
        725 });
        726};
        727
        728
        729/**
        730 * Locates a DOM element so that commands may be issued against it using the
        731 * {@link webdriver.WebElement} class. This is accomplished by storing a
        732 * reference to the element in an object on the element's ownerDocument.
        733 * {@link #executeScript} will then be used to create a WebElement from this
        734 * reference. This requires this driver to currently be focused on the
        735 * ownerDocument's window+frame.
        736
        737 * @param {!Element} element The element to locate.
        738 * @return {!webdriver.promise.Promise} A promise that will be resolved
        739 * with the located WebElement.
        740 * @private
        741 */
        742webdriver.WebDriver.prototype.findDomElement_ = function(element) {
        743 var doc = element.ownerDocument;
        744 var store = doc['$webdriver$'] = doc['$webdriver$'] || {};
        745 var id = Math.floor(Math.random() * goog.now()).toString(36);
        746 store[id] = element;
        747 element[id] = id;
        748
        749 function cleanUp() {
        750 delete store[id];
        751 }
        752
        753 function lookupElement(id) {
        754 var store = document['$webdriver$'];
        755 if (!store) {
        756 return null;
        757 }
        758
        759 var element = store[id];
        760 if (!element || element[id] !== id) {
        761 return [];
        762 }
        763 return [element];
        764 }
        765
        766 return this.executeScript(lookupElement, id).
        767 then(function(value) {
        768 cleanUp();
        769 if (value.length && !(value[0] instanceof webdriver.WebElement)) {
        770 throw new Error('JS locator script result was not a WebElement');
        771 }
        772 return value;
        773 }, cleanUp);
        774};
        775
        776
        777/**
        778 * Schedules a command to test if an element is present on the page.
        779 *
        780 * <p>If given a DOM element, this function will check if it belongs to the
        781 * document the driver is currently focused on. Otherwise, the function will
        782 * test if at least one element can be found with the given search criteria.
        783 *
        784 * @param {!(webdriver.Locator|webdriver.By.Hash|Element|
        785 * Function)} locatorOrElement The locator to use, or the actual
        786 * DOM element to be located by the server.
        787 * @return {!webdriver.promise.Promise.<boolean>} A promise that will resolve
        788 * with whether the element is present on the page.
        789 */
        790webdriver.WebDriver.prototype.isElementPresent = function(locatorOrElement) {
        791 var findElement =
        792 ('nodeType' in locatorOrElement && 'ownerDocument' in locatorOrElement) ?
        793 this.findDomElement_(/** @type {!Element} */ (locatorOrElement)) :
        794 this.findElements.apply(this, arguments);
        795 return findElement.then(function(result) {
        796 return !!result.length;
        797 });
        798};
        799
        800
        801/**
        802 * Schedule a command to search for multiple elements on the page.
        803 *
        804 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator
        805 * strategy to use when searching for the element.
        806 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
        807 * promise that will resolve to an array of WebElements.
        808 */
        809webdriver.WebDriver.prototype.findElements = function(locator) {
        810 locator = webdriver.Locator.checkLocator(locator);
        811 if (goog.isFunction(locator)) {
        812 return this.findElementsInternal_(locator, this);
        813 } else {
        814 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENTS).
        815 setParameter('using', locator.using).
        816 setParameter('value', locator.value);
        817 return this.schedule(command, 'WebDriver.findElements(' + locator + ')');
        818 }
        819};
        820
        821
        822/**
        823 * @param {!Function} locatorFn The locator function to use.
        824 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search
        825 * context.
        826 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
        827 * promise that will resolve to an array of WebElements.
        828 * @private
        829 */
        830webdriver.WebDriver.prototype.findElementsInternal_ = function(
        831 locatorFn, context) {
        832 return this.call(goog.partial(locatorFn, context)).then(function(result) {
        833 if (result instanceof webdriver.WebElement) {
        834 return [result];
        835 }
        836
        837 if (!goog.isArray(result)) {
        838 return [];
        839 }
        840
        841 return goog.array.filter(result, function(item) {
        842 return item instanceof webdriver.WebElement;
        843 });
        844 });
        845};
        846
        847
        848/**
        849 * Schedule a command to take a screenshot. The driver makes a best effort to
        850 * return a screenshot of the following, in order of preference:
        851 * <ol>
        852 * <li>Entire page
        853 * <li>Current window
        854 * <li>Visible portion of the current frame
        855 * <li>The screenshot of the entire display containing the browser
        856 * </ol>
        857 *
        858 * @return {!webdriver.promise.Promise} A promise that will be resolved to the
        859 * screenshot as a base-64 encoded PNG.
        860 */
        861webdriver.WebDriver.prototype.takeScreenshot = function() {
        862 return this.schedule(new webdriver.Command(webdriver.CommandName.SCREENSHOT),
        863 'WebDriver.takeScreenshot()');
        864};
        865
        866
        867/**
        868 * @return {!webdriver.WebDriver.Options} The options interface for this
        869 * instance.
        870 */
        871webdriver.WebDriver.prototype.manage = function() {
        872 return new webdriver.WebDriver.Options(this);
        873};
        874
        875
        876/**
        877 * @return {!webdriver.WebDriver.Navigation} The navigation interface for this
        878 * instance.
        879 */
        880webdriver.WebDriver.prototype.navigate = function() {
        881 return new webdriver.WebDriver.Navigation(this);
        882};
        883
        884
        885/**
        886 * @return {!webdriver.WebDriver.TargetLocator} The target locator interface for
        887 * this instance.
        888 */
        889webdriver.WebDriver.prototype.switchTo = function() {
        890 return new webdriver.WebDriver.TargetLocator(this);
        891};
        892
        893
        894
        895/**
        896 * Interface for navigating back and forth in the browser history.
        897 * @param {!webdriver.WebDriver} driver The parent driver.
        898 * @constructor
        899 */
        900webdriver.WebDriver.Navigation = function(driver) {
        901
        902 /** @private {!webdriver.WebDriver} */
        903 this.driver_ = driver;
        904};
        905
        906
        907/**
        908 * Schedules a command to navigate to a new URL.
        909 * @param {string} url The URL to navigate to.
        910 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        911 * URL has been loaded.
        912 */
        913webdriver.WebDriver.Navigation.prototype.to = function(url) {
        914 return this.driver_.schedule(
        915 new webdriver.Command(webdriver.CommandName.GET).
        916 setParameter('url', url),
        917 'WebDriver.navigate().to(' + url + ')');
        918};
        919
        920
        921/**
        922 * Schedules a command to move backwards in the browser history.
        923 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        924 * navigation event has completed.
        925 */
        926webdriver.WebDriver.Navigation.prototype.back = function() {
        927 return this.driver_.schedule(
        928 new webdriver.Command(webdriver.CommandName.GO_BACK),
        929 'WebDriver.navigate().back()');
        930};
        931
        932
        933/**
        934 * Schedules a command to move forwards in the browser history.
        935 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        936 * navigation event has completed.
        937 */
        938webdriver.WebDriver.Navigation.prototype.forward = function() {
        939 return this.driver_.schedule(
        940 new webdriver.Command(webdriver.CommandName.GO_FORWARD),
        941 'WebDriver.navigate().forward()');
        942};
        943
        944
        945/**
        946 * Schedules a command to refresh the current page.
        947 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        948 * navigation event has completed.
        949 */
        950webdriver.WebDriver.Navigation.prototype.refresh = function() {
        951 return this.driver_.schedule(
        952 new webdriver.Command(webdriver.CommandName.REFRESH),
        953 'WebDriver.navigate().refresh()');
        954};
        955
        956
        957
        958/**
        959 * Provides methods for managing browser and driver state.
        960 * @param {!webdriver.WebDriver} driver The parent driver.
        961 * @constructor
        962 */
        963webdriver.WebDriver.Options = function(driver) {
        964
        965 /** @private {!webdriver.WebDriver} */
        966 this.driver_ = driver;
        967};
        968
        969
        970/**
        971 * Schedules a command to add a cookie.
        972 * @param {string} name The cookie name.
        973 * @param {string} value The cookie value.
        974 * @param {string=} opt_path The cookie path.
        975 * @param {string=} opt_domain The cookie domain.
        976 * @param {boolean=} opt_isSecure Whether the cookie is secure.
        977 * @param {(number|!Date)=} opt_expiry When the cookie expires. If specified as
        978 * a number, should be in milliseconds since midnight, January 1, 1970 UTC.
        979 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        980 * cookie has been added to the page.
        981 */
        982webdriver.WebDriver.Options.prototype.addCookie = function(
        983 name, value, opt_path, opt_domain, opt_isSecure, opt_expiry) {
        984 // We do not allow '=' or ';' in the name.
        985 if (/[;=]/.test(name)) {
        986 throw Error('Invalid cookie name "' + name + '"');
        987 }
        988
        989 // We do not allow ';' in value.
        990 if (/;/.test(value)) {
        991 throw Error('Invalid cookie value "' + value + '"');
        992 }
        993
        994 var cookieString = name + '=' + value +
        995 (opt_domain ? ';domain=' + opt_domain : '') +
        996 (opt_path ? ';path=' + opt_path : '') +
        997 (opt_isSecure ? ';secure' : '');
        998
        999 var expiry;
        1000 if (goog.isDef(opt_expiry)) {
        1001 var expiryDate;
        1002 if (goog.isNumber(opt_expiry)) {
        1003 expiryDate = new Date(opt_expiry);
        1004 } else {
        1005 expiryDate = /** @type {!Date} */ (opt_expiry);
        1006 opt_expiry = expiryDate.getTime();
        1007 }
        1008 cookieString += ';expires=' + expiryDate.toUTCString();
        1009 // Convert from milliseconds to seconds.
        1010 expiry = Math.floor(/** @type {number} */ (opt_expiry) / 1000);
        1011 }
        1012
        1013 return this.driver_.schedule(
        1014 new webdriver.Command(webdriver.CommandName.ADD_COOKIE).
        1015 setParameter('cookie', {
        1016 'name': name,
        1017 'value': value,
        1018 'path': opt_path,
        1019 'domain': opt_domain,
        1020 'secure': !!opt_isSecure,
        1021 'expiry': expiry
        1022 }),
        1023 'WebDriver.manage().addCookie(' + cookieString + ')');
        1024};
        1025
        1026
        1027/**
        1028 * Schedules a command to delete all cookies visible to the current page.
        1029 * @return {!webdriver.promise.Promise} A promise that will be resolved when all
        1030 * cookies have been deleted.
        1031 */
        1032webdriver.WebDriver.Options.prototype.deleteAllCookies = function() {
        1033 return this.driver_.schedule(
        1034 new webdriver.Command(webdriver.CommandName.DELETE_ALL_COOKIES),
        1035 'WebDriver.manage().deleteAllCookies()');
        1036};
        1037
        1038
        1039/**
        1040 * Schedules a command to delete the cookie with the given name. This command is
        1041 * a no-op if there is no cookie with the given name visible to the current
        1042 * page.
        1043 * @param {string} name The name of the cookie to delete.
        1044 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1045 * cookie has been deleted.
        1046 */
        1047webdriver.WebDriver.Options.prototype.deleteCookie = function(name) {
        1048 return this.driver_.schedule(
        1049 new webdriver.Command(webdriver.CommandName.DELETE_COOKIE).
        1050 setParameter('name', name),
        1051 'WebDriver.manage().deleteCookie(' + name + ')');
        1052};
        1053
        1054
        1055/**
        1056 * Schedules a command to retrieve all cookies visible to the current page.
        1057 * Each cookie will be returned as a JSON object as described by the WebDriver
        1058 * wire protocol.
        1059 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1060 * cookies visible to the current page.
        1061 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object
        1062 */
        1063webdriver.WebDriver.Options.prototype.getCookies = function() {
        1064 return this.driver_.schedule(
        1065 new webdriver.Command(webdriver.CommandName.GET_ALL_COOKIES),
        1066 'WebDriver.manage().getCookies()');
        1067};
        1068
        1069
        1070/**
        1071 * Schedules a command to retrieve the cookie with the given name. Returns null
        1072 * if there is no such cookie. The cookie will be returned as a JSON object as
        1073 * described by the WebDriver wire protocol.
        1074 * @param {string} name The name of the cookie to retrieve.
        1075 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1076 * named cookie, or {@code null} if there is no such cookie.
        1077 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object
        1078 */
        1079webdriver.WebDriver.Options.prototype.getCookie = function(name) {
        1080 return this.getCookies().then(function(cookies) {
        1081 return goog.array.find(cookies, function(cookie) {
        1082 return cookie && cookie['name'] == name;
        1083 });
        1084 });
        1085};
        1086
        1087
        1088/**
        1089 * @return {!webdriver.WebDriver.Logs} The interface for managing driver
        1090 * logs.
        1091 */
        1092webdriver.WebDriver.Options.prototype.logs = function() {
        1093 return new webdriver.WebDriver.Logs(this.driver_);
        1094};
        1095
        1096
        1097/**
        1098 * @return {!webdriver.WebDriver.Timeouts} The interface for managing driver
        1099 * timeouts.
        1100 */
        1101webdriver.WebDriver.Options.prototype.timeouts = function() {
        1102 return new webdriver.WebDriver.Timeouts(this.driver_);
        1103};
        1104
        1105
        1106/**
        1107 * @return {!webdriver.WebDriver.Window} The interface for managing the
        1108 * current window.
        1109 */
        1110webdriver.WebDriver.Options.prototype.window = function() {
        1111 return new webdriver.WebDriver.Window(this.driver_);
        1112};
        1113
        1114
        1115
        1116/**
        1117 * An interface for managing timeout behavior for WebDriver instances.
        1118 * @param {!webdriver.WebDriver} driver The parent driver.
        1119 * @constructor
        1120 */
        1121webdriver.WebDriver.Timeouts = function(driver) {
        1122
        1123 /** @private {!webdriver.WebDriver} */
        1124 this.driver_ = driver;
        1125};
        1126
        1127
        1128/**
        1129 * Specifies the amount of time the driver should wait when searching for an
        1130 * element if it is not immediately present.
        1131 * <p/>
        1132 * When searching for a single element, the driver should poll the page
        1133 * until the element has been found, or this timeout expires before failing
        1134 * with a {@code bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching
        1135 * for multiple elements, the driver should poll the page until at least one
        1136 * element has been found or this timeout has expired.
        1137 * <p/>
        1138 * Setting the wait timeout to 0 (its default value), disables implicit
        1139 * waiting.
        1140 * <p/>
        1141 * Increasing the implicit wait timeout should be used judiciously as it
        1142 * will have an adverse effect on test run time, especially when used with
        1143 * slower location strategies like XPath.
        1144 *
        1145 * @param {number} ms The amount of time to wait, in milliseconds.
        1146 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1147 * implicit wait timeout has been set.
        1148 */
        1149webdriver.WebDriver.Timeouts.prototype.implicitlyWait = function(ms) {
        1150 return this.driver_.schedule(
        1151 new webdriver.Command(webdriver.CommandName.IMPLICITLY_WAIT).
        1152 setParameter('ms', ms < 0 ? 0 : ms),
        1153 'WebDriver.manage().timeouts().implicitlyWait(' + ms + ')');
        1154};
        1155
        1156
        1157/**
        1158 * Sets the amount of time to wait, in milliseconds, for an asynchronous script
        1159 * to finish execution before returning an error. If the timeout is less than or
        1160 * equal to 0, the script will be allowed to run indefinitely.
        1161 *
        1162 * @param {number} ms The amount of time to wait, in milliseconds.
        1163 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1164 * script timeout has been set.
        1165 */
        1166webdriver.WebDriver.Timeouts.prototype.setScriptTimeout = function(ms) {
        1167 return this.driver_.schedule(
        1168 new webdriver.Command(webdriver.CommandName.SET_SCRIPT_TIMEOUT).
        1169 setParameter('ms', ms < 0 ? 0 : ms),
        1170 'WebDriver.manage().timeouts().setScriptTimeout(' + ms + ')');
        1171};
        1172
        1173
        1174/**
        1175 * Sets the amount of time to wait for a page load to complete before returning
        1176 * an error. If the timeout is negative, page loads may be indefinite.
        1177 * @param {number} ms The amount of time to wait, in milliseconds.
        1178 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        1179 * the timeout has been set.
        1180 */
        1181webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout = function(ms) {
        1182 return this.driver_.schedule(
        1183 new webdriver.Command(webdriver.CommandName.SET_TIMEOUT).
        1184 setParameter('type', 'page load').
        1185 setParameter('ms', ms),
        1186 'WebDriver.manage().timeouts().pageLoadTimeout(' + ms + ')');
        1187};
        1188
        1189
        1190
        1191/**
        1192 * An interface for managing the current window.
        1193 * @param {!webdriver.WebDriver} driver The parent driver.
        1194 * @constructor
        1195 */
        1196webdriver.WebDriver.Window = function(driver) {
        1197
        1198 /** @private {!webdriver.WebDriver} */
        1199 this.driver_ = driver;
        1200};
        1201
        1202
        1203/**
        1204 * Retrieves the window's current position, relative to the top left corner of
        1205 * the screen.
        1206 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1207 * window's position in the form of a {x:number, y:number} object literal.
        1208 */
        1209webdriver.WebDriver.Window.prototype.getPosition = function() {
        1210 return this.driver_.schedule(
        1211 new webdriver.Command(webdriver.CommandName.GET_WINDOW_POSITION).
        1212 setParameter('windowHandle', 'current'),
        1213 'WebDriver.manage().window().getPosition()');
        1214};
        1215
        1216
        1217/**
        1218 * Repositions the current window.
        1219 * @param {number} x The desired horizontal position, relative to the left side
        1220 * of the screen.
        1221 * @param {number} y The desired vertical position, relative to the top of the
        1222 * of the screen.
        1223 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1224 * command has completed.
        1225 */
        1226webdriver.WebDriver.Window.prototype.setPosition = function(x, y) {
        1227 return this.driver_.schedule(
        1228 new webdriver.Command(webdriver.CommandName.SET_WINDOW_POSITION).
        1229 setParameter('windowHandle', 'current').
        1230 setParameter('x', x).
        1231 setParameter('y', y),
        1232 'WebDriver.manage().window().setPosition(' + x + ', ' + y + ')');
        1233};
        1234
        1235
        1236/**
        1237 * Retrieves the window's current size.
        1238 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1239 * window's size in the form of a {width:number, height:number} object
        1240 * literal.
        1241 */
        1242webdriver.WebDriver.Window.prototype.getSize = function() {
        1243 return this.driver_.schedule(
        1244 new webdriver.Command(webdriver.CommandName.GET_WINDOW_SIZE).
        1245 setParameter('windowHandle', 'current'),
        1246 'WebDriver.manage().window().getSize()');
        1247};
        1248
        1249
        1250/**
        1251 * Resizes the current window.
        1252 * @param {number} width The desired window width.
        1253 * @param {number} height The desired window height.
        1254 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1255 * command has completed.
        1256 */
        1257webdriver.WebDriver.Window.prototype.setSize = function(width, height) {
        1258 return this.driver_.schedule(
        1259 new webdriver.Command(webdriver.CommandName.SET_WINDOW_SIZE).
        1260 setParameter('windowHandle', 'current').
        1261 setParameter('width', width).
        1262 setParameter('height', height),
        1263 'WebDriver.manage().window().setSize(' + width + ', ' + height + ')');
        1264};
        1265
        1266
        1267/**
        1268 * Maximizes the current window.
        1269 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1270 * command has completed.
        1271 */
        1272webdriver.WebDriver.Window.prototype.maximize = function() {
        1273 return this.driver_.schedule(
        1274 new webdriver.Command(webdriver.CommandName.MAXIMIZE_WINDOW).
        1275 setParameter('windowHandle', 'current'),
        1276 'WebDriver.manage().window().maximize()');
        1277};
        1278
        1279
        1280/**
        1281 * Interface for managing WebDriver log records.
        1282 * @param {!webdriver.WebDriver} driver The parent driver.
        1283 * @constructor
        1284 */
        1285webdriver.WebDriver.Logs = function(driver) {
        1286
        1287 /** @private {!webdriver.WebDriver} */
        1288 this.driver_ = driver;
        1289};
        1290
        1291
        1292/**
        1293 * Fetches available log entries for the given type.
        1294 *
        1295 * <p/>Note that log buffers are reset after each call, meaning that
        1296 * available log entries correspond to those entries not yet returned for a
        1297 * given log type. In practice, this means that this call will return the
        1298 * available log entries since the last call, or from the start of the
        1299 * session.
        1300 *
        1301 * @param {!webdriver.logging.Type} type The desired log type.
        1302 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Entry>>} A
        1303 * promise that will resolve to a list of log entries for the specified
        1304 * type.
        1305 */
        1306webdriver.WebDriver.Logs.prototype.get = function(type) {
        1307 return this.driver_.schedule(
        1308 new webdriver.Command(webdriver.CommandName.GET_LOG).
        1309 setParameter('type', type),
        1310 'WebDriver.manage().logs().get(' + type + ')').
        1311 then(function(entries) {
        1312 return goog.array.map(entries, function(entry) {
        1313 if (!(entry instanceof webdriver.logging.Entry)) {
        1314 return new webdriver.logging.Entry(
        1315 entry['level'], entry['message'], entry['timestamp']);
        1316 }
        1317 return entry;
        1318 });
        1319 });
        1320};
        1321
        1322
        1323/**
        1324 * Retrieves the log types available to this driver.
        1325 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Type>>} A
        1326 * promise that will resolve to a list of available log types.
        1327 */
        1328webdriver.WebDriver.Logs.prototype.getAvailableLogTypes = function() {
        1329 return this.driver_.schedule(
        1330 new webdriver.Command(webdriver.CommandName.GET_AVAILABLE_LOG_TYPES),
        1331 'WebDriver.manage().logs().getAvailableLogTypes()');
        1332};
        1333
        1334
        1335
        1336/**
        1337 * An interface for changing the focus of the driver to another frame or window.
        1338 * @param {!webdriver.WebDriver} driver The parent driver.
        1339 * @constructor
        1340 */
        1341webdriver.WebDriver.TargetLocator = function(driver) {
        1342
        1343 /** @private {!webdriver.WebDriver} */
        1344 this.driver_ = driver;
        1345};
        1346
        1347
        1348/**
        1349 * Schedules a command retrieve the {@code document.activeElement} element on
        1350 * the current document, or {@code document.body} if activeElement is not
        1351 * available.
        1352 * @return {!webdriver.WebElement} The active element.
        1353 */
        1354webdriver.WebDriver.TargetLocator.prototype.activeElement = function() {
        1355 var id = this.driver_.schedule(
        1356 new webdriver.Command(webdriver.CommandName.GET_ACTIVE_ELEMENT),
        1357 'WebDriver.switchTo().activeElement()');
        1358 return new webdriver.WebElement(this.driver_, id);
        1359};
        1360
        1361
        1362/**
        1363 * Schedules a command to switch focus of all future commands to the first frame
        1364 * on the page.
        1365 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1366 * driver has changed focus to the default content.
        1367 */
        1368webdriver.WebDriver.TargetLocator.prototype.defaultContent = function() {
        1369 return this.driver_.schedule(
        1370 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME).
        1371 setParameter('id', null),
        1372 'WebDriver.switchTo().defaultContent()');
        1373};
        1374
        1375
        1376/**
        1377 * Schedules a command to switch the focus of all future commands to another
        1378 * frame on the page.
        1379 * <p/>
        1380 * If the frame is specified by a number, the command will switch to the frame
        1381 * by its (zero-based) index into the {@code window.frames} collection.
        1382 * <p/>
        1383 * If the frame is specified by a string, the command will select the frame by
        1384 * its name or ID. To select sub-frames, simply separate the frame names/IDs by
        1385 * dots. As an example, "main.child" will select the frame with the name "main"
        1386 * and then its child "child".
        1387 * <p/>
        1388 * If the specified frame can not be found, the deferred result will errback
        1389 * with a {@code bot.ErrorCode.NO_SUCH_FRAME} error.
        1390 * @param {string|number} nameOrIndex The frame locator.
        1391 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1392 * driver has changed focus to the specified frame.
        1393 */
        1394webdriver.WebDriver.TargetLocator.prototype.frame = function(nameOrIndex) {
        1395 return this.driver_.schedule(
        1396 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME).
        1397 setParameter('id', nameOrIndex),
        1398 'WebDriver.switchTo().frame(' + nameOrIndex + ')');
        1399};
        1400
        1401
        1402/**
        1403 * Schedules a command to switch the focus of all future commands to another
        1404 * window. Windows may be specified by their {@code window.name} attribute or
        1405 * by its handle (as returned by {@code webdriver.WebDriver#getWindowHandles}).
        1406 * <p/>
        1407 * If the specificed window can not be found, the deferred result will errback
        1408 * with a {@code bot.ErrorCode.NO_SUCH_WINDOW} error.
        1409 * @param {string} nameOrHandle The name or window handle of the window to
        1410 * switch focus to.
        1411 * @return {!webdriver.promise.Promise} A promise that will be resolved when the
        1412 * driver has changed focus to the specified window.
        1413 */
        1414webdriver.WebDriver.TargetLocator.prototype.window = function(nameOrHandle) {
        1415 return this.driver_.schedule(
        1416 new webdriver.Command(webdriver.CommandName.SWITCH_TO_WINDOW).
        1417 setParameter('name', nameOrHandle),
        1418 'WebDriver.switchTo().window(' + nameOrHandle + ')');
        1419};
        1420
        1421
        1422/**
        1423 * Schedules a command to change focus to the active alert dialog. This command
        1424 * will return a {@link bot.ErrorCode.NO_MODAL_DIALOG_OPEN} error if a modal
        1425 * dialog is not currently open.
        1426 * @return {!webdriver.Alert} The open alert.
        1427 */
        1428webdriver.WebDriver.TargetLocator.prototype.alert = function() {
        1429 var text = this.driver_.schedule(
        1430 new webdriver.Command(webdriver.CommandName.GET_ALERT_TEXT),
        1431 'WebDriver.switchTo().alert()');
        1432 return new webdriver.Alert(this.driver_, text);
        1433};
        1434
        1435
        1436/**
        1437 * Simulate pressing many keys at once in a "chord". Takes a sequence of
        1438 * {@link webdriver.Key}s or strings, appends each of the values to a string,
        1439 * and adds the chord termination key ({@link webdriver.Key.NULL}) and returns
        1440 * the resultant string.
        1441 *
        1442 * Note: when the low-level webdriver key handlers see Keys.NULL, active
        1443 * modifier keys (CTRL/ALT/SHIFT/etc) release via a keyup event.
        1444 *
        1445 * @param {...string} var_args The key sequence to concatenate.
        1446 * @return {string} The null-terminated key sequence.
        1447 * @see http://code.google.com/p/webdriver/issues/detail?id=79
        1448 */
        1449webdriver.Key.chord = function(var_args) {
        1450 var sequence = goog.array.reduce(
        1451 goog.array.slice(arguments, 0),
        1452 function(str, key) {
        1453 return str + key;
        1454 }, '');
        1455 sequence += webdriver.Key.NULL;
        1456 return sequence;
        1457};
        1458
        1459
        1460//////////////////////////////////////////////////////////////////////////////
        1461//
        1462// webdriver.WebElement
        1463//
        1464//////////////////////////////////////////////////////////////////////////////
        1465
        1466
        1467
        1468/**
        1469 * Represents a DOM element. WebElements can be found by searching from the
        1470 * document root using a {@code webdriver.WebDriver} instance, or by searching
        1471 * under another {@code webdriver.WebElement}:
        1472 * <pre><code>
        1473 * driver.get('http://www.google.com');
        1474 * var searchForm = driver.findElement(By.tagName('form'));
        1475 * var searchBox = searchForm.findElement(By.name('q'));
        1476 * searchBox.sendKeys('webdriver');
        1477 * </code></pre>
        1478 *
        1479 * The WebElement is implemented as a promise for compatibility with the promise
        1480 * API. It will always resolve itself when its internal state has been fully
        1481 * resolved and commands may be issued against the element. This can be used to
        1482 * catch errors when an element cannot be located on the page:
        1483 * <pre><code>
        1484 * driver.findElement(By.id('not-there')).then(function(element) {
        1485 * alert('Found an element that was not expected to be there!');
        1486 * }, function(error) {
        1487 * alert('The element was not found, as expected');
        1488 * });
        1489 * </code></pre>
        1490 *
        1491 * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this
        1492 * element.
        1493 * @param {!(string|webdriver.promise.Promise)} id Either the opaque ID for the
        1494 * underlying DOM element assigned by the server, or a promise that will
        1495 * resolve to that ID or another WebElement.
        1496 * @constructor
        1497 * @extends {webdriver.promise.Deferred}
        1498 */
        1499webdriver.WebElement = function(driver, id) {
        1500 webdriver.promise.Deferred.call(this, null, driver.controlFlow());
        1501
        1502 /**
        1503 * The parent WebDriver instance for this element.
        1504 * @private {!webdriver.WebDriver}
        1505 */
        1506 this.driver_ = driver;
        1507
        1508 // This class is responsible for resolving itself; delete the resolve and
        1509 // reject methods so they may not be accessed by consumers of this class.
        1510 var fulfill = goog.partial(this.fulfill, this);
        1511 var reject = this.reject;
        1512 delete this.promise;
        1513 delete this.fulfill;
        1514 delete this.reject;
        1515
        1516 /**
        1517 * A promise that resolves to the JSON representation of this WebElement's
        1518 * ID, as defined by the WebDriver wire protocol.
        1519 * @private {!webdriver.promise.Promise}
        1520 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
        1521 */
        1522 this.id_ = webdriver.promise.when(id, function(id) {
        1523 if (id instanceof webdriver.WebElement) {
        1524 return id.id_;
        1525 } else if (goog.isDef(id[webdriver.WebElement.ELEMENT_KEY])) {
        1526 return id;
        1527 }
        1528
        1529 var json = {};
        1530 json[webdriver.WebElement.ELEMENT_KEY] = id;
        1531 return json;
        1532 });
        1533
        1534 // This WebElement should not be resolved until its ID has been
        1535 // fully resolved.
        1536 this.id_.then(fulfill, reject);
        1537};
        1538goog.inherits(webdriver.WebElement, webdriver.promise.Deferred);
        1539
        1540
        1541/**
        1542 * The property key used in the wire protocol to indicate that a JSON object
        1543 * contains the ID of a WebElement.
        1544 * @type {string}
        1545 * @const
        1546 */
        1547webdriver.WebElement.ELEMENT_KEY = 'ELEMENT';
        1548
        1549
        1550/**
        1551 * Compares to WebElements for equality.
        1552 * @param {!webdriver.WebElement} a A WebElement.
        1553 * @param {!webdriver.WebElement} b A WebElement.
        1554 * @return {!webdriver.promise.Promise} A promise that will be resolved to
        1555 * whether the two WebElements are equal.
        1556 */
        1557webdriver.WebElement.equals = function(a, b) {
        1558 if (a == b) {
        1559 return webdriver.promise.fulfilled(true);
        1560 }
        1561 return webdriver.promise.fullyResolved([a.id_, b.id_]).then(function(ids) {
        1562 // If the two element's have the same ID, they should be considered
        1563 // equal. Otherwise, they may still be equivalent, but we'll need to
        1564 // ask the server to check for us.
        1565 if (ids[0][webdriver.WebElement.ELEMENT_KEY] ==
        1566 ids[1][webdriver.WebElement.ELEMENT_KEY]) {
        1567 return true;
        1568 }
        1569
        1570 var command = new webdriver.Command(
        1571 webdriver.CommandName.ELEMENT_EQUALS);
        1572 command.setParameter('other', b);
        1573 return a.schedule_(command, 'webdriver.WebElement.equals()');
        1574 });
        1575};
        1576
        1577
        1578/**
        1579 * @return {!webdriver.WebDriver} The parent driver for this instance.
        1580 */
        1581webdriver.WebElement.prototype.getDriver = function() {
        1582 return this.driver_;
        1583};
        1584
        1585
        1586/**
        1587 * @return {!webdriver.promise.Promise} A promise that resolves to this
        1588 * element's JSON representation as defined by the WebDriver wire protocol.
        1589 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol
        1590 */
        1591webdriver.WebElement.prototype.toWireValue = function() {
        1592 return this.id_;
        1593};
        1594
        1595
        1596/**
        1597 * Schedules a command that targets this element with the parent WebDriver
        1598 * instance. Will ensure this element's ID is included in the command parameters
        1599 * under the "id" key.
        1600 * @param {!webdriver.Command} command The command to schedule.
        1601 * @param {string} description A description of the command for debugging.
        1602 * @return {!webdriver.promise.Promise} A promise that will be resolved with
        1603 * the command result.
        1604 * @see webdriver.WebDriver.prototype.schedule
        1605 * @private
        1606 */
        1607webdriver.WebElement.prototype.schedule_ = function(command, description) {
        1608 command.setParameter('id', this.id_);
        1609 return this.driver_.schedule(command, description);
        1610};
        1611
        1612
        1613/**
        1614 * Schedule a command to find a descendant of this element. If the element
        1615 * cannot be found, a {@code bot.ErrorCode.NO_SUCH_ELEMENT} result will
        1616 * be returned by the driver. Unlike other commands, this error cannot be
        1617 * suppressed. In other words, scheduling a command to find an element doubles
        1618 * as an assert that the element is present on the page. To test whether an
        1619 * element is present on the page, use {@code #isElementPresent} instead.
        1620 *
        1621 * <p>The search criteria for an element may be defined using one of the
        1622 * factories in the {@link webdriver.By} namespace, or as a short-hand
        1623 * {@link webdriver.By.Hash} object. For example, the following two statements
        1624 * are equivalent:
        1625 * <code><pre>
        1626 * var e1 = element.findElement(By.id('foo'));
        1627 * var e2 = element.findElement({id:'foo'});
        1628 * </pre></code>
        1629 *
        1630 * <p>You may also provide a custom locator function, which takes as input
        1631 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a
        1632 * promise that will resolve to a WebElement. For example, to find the first
        1633 * visible link on a page, you could write:
        1634 * <code><pre>
        1635 * var link = element.findElement(firstVisibleLink);
        1636 *
        1637 * function firstVisibleLink(element) {
        1638 * var links = element.findElements(By.tagName('a'));
        1639 * return webdriver.promise.filter(links, function(link) {
        1640 * return links.isDisplayed();
        1641 * }).then(function(visibleLinks) {
        1642 * return visibleLinks[0];
        1643 * });
        1644 * }
        1645 * </pre></code>
        1646 *
        1647 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
        1648 * locator strategy to use when searching for the element.
        1649 * @return {!webdriver.WebElement} A WebElement that can be used to issue
        1650 * commands against the located element. If the element is not found, the
        1651 * element will be invalidated and all scheduled commands aborted.
        1652 */
        1653webdriver.WebElement.prototype.findElement = function(locator) {
        1654 locator = webdriver.Locator.checkLocator(locator);
        1655 var id;
        1656 if (goog.isFunction(locator)) {
        1657 id = this.driver_.findElementInternal_(locator, this);
        1658 } else {
        1659 var command = new webdriver.Command(
        1660 webdriver.CommandName.FIND_CHILD_ELEMENT).
        1661 setParameter('using', locator.using).
        1662 setParameter('value', locator.value);
        1663 id = this.schedule_(command, 'WebElement.findElement(' + locator + ')');
        1664 }
        1665 return new webdriver.WebElement(this.driver_, id);
        1666};
        1667
        1668
        1669/**
        1670 * Schedules a command to test if there is at least one descendant of this
        1671 * element that matches the given search criteria.
        1672 *
        1673 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
        1674 * locator strategy to use when searching for the element.
        1675 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
        1676 * resolved with whether an element could be located on the page.
        1677 */
        1678webdriver.WebElement.prototype.isElementPresent = function(locator) {
        1679 return this.findElements(locator).then(function(result) {
        1680 return !!result.length;
        1681 });
        1682};
        1683
        1684
        1685/**
        1686 * Schedules a command to find all of the descendants of this element that
        1687 * match the given search criteria.
        1688 *
        1689 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
        1690 * locator strategy to use when searching for the elements.
        1691 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
        1692 * promise that will resolve to an array of WebElements.
        1693 */
        1694webdriver.WebElement.prototype.findElements = function(locator) {
        1695 locator = webdriver.Locator.checkLocator(locator);
        1696 if (goog.isFunction(locator)) {
        1697 return this.driver_.findElementsInternal_(locator, this);
        1698 } else {
        1699 var command = new webdriver.Command(
        1700 webdriver.CommandName.FIND_CHILD_ELEMENTS).
        1701 setParameter('using', locator.using).
        1702 setParameter('value', locator.value);
        1703 return this.schedule_(command, 'WebElement.findElements(' + locator + ')');
        1704 }
        1705};
        1706
        1707
        1708/**
        1709 * Schedules a command to click on this element.
        1710 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        1711 * the click command has completed.
        1712 */
        1713webdriver.WebElement.prototype.click = function() {
        1714 return this.schedule_(
        1715 new webdriver.Command(webdriver.CommandName.CLICK_ELEMENT),
        1716 'WebElement.click()');
        1717};
        1718
        1719
        1720/**
        1721 * Schedules a command to type a sequence on the DOM element represented by this
        1722 * instance.
        1723 * <p/>
        1724 * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
        1725 * processed in the keysequence, that key state is toggled until one of the
        1726 * following occurs:
        1727 * <ul>
        1728 * <li>The modifier key is encountered again in the sequence. At this point the
        1729 * state of the key is toggled (along with the appropriate keyup/down events).
        1730 * </li>
        1731 * <li>The {@code webdriver.Key.NULL} key is encountered in the sequence. When
        1732 * this key is encountered, all modifier keys current in the down state are
        1733 * released (with accompanying keyup events). The NULL key can be used to
        1734 * simulate common keyboard shortcuts:
        1735 * <code><pre>
        1736 * element.sendKeys("text was",
        1737 * webdriver.Key.CONTROL, "a", webdriver.Key.NULL,
        1738 * "now text is");
        1739 * // Alternatively:
        1740 * element.sendKeys("text was",
        1741 * webdriver.Key.chord(webdriver.Key.CONTROL, "a"),
        1742 * "now text is");
        1743 * </pre></code></li>
        1744 * <li>The end of the keysequence is encountered. When there are no more keys
        1745 * to type, all depressed modifier keys are released (with accompanying keyup
        1746 * events).
        1747 * </li>
        1748 * </ul>
        1749 * <strong>Note:</strong> On browsers where native keyboard events are not yet
        1750 * supported (e.g. Firefox on OS X), key events will be synthesized. Special
        1751 * punctionation keys will be synthesized according to a standard QWERTY en-us
        1752 * keyboard layout.
        1753 *
        1754 * @param {...string} var_args The sequence of keys to
        1755 * type. All arguments will be joined into a single sequence (var_args is
        1756 * permitted for convenience).
        1757 * @return {!webdriver.promise.Promise} A promise that will be resolved when all
        1758 * keys have been typed.
        1759 */
        1760webdriver.WebElement.prototype.sendKeys = function(var_args) {
        1761 // Coerce every argument to a string. This protects us from users that
        1762 // ignore the jsdoc and give us a number (which ends up causing problems on
        1763 // the server, which requires strings).
        1764 var keys = webdriver.promise.fullyResolved(goog.array.slice(arguments, 0)).
        1765 then(function(args) {
        1766 return goog.array.map(goog.array.slice(args, 0), function(key) {
        1767 return key + '';
        1768 });
        1769 });
        1770 return this.schedule_(
        1771 new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT).
        1772 setParameter('value', keys),
        1773 'WebElement.sendKeys(' + keys + ')');
        1774};
        1775
        1776
        1777/**
        1778 * Schedules a command to query for the tag/node name of this element.
        1779 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1780 * element's tag name.
        1781 */
        1782webdriver.WebElement.prototype.getTagName = function() {
        1783 return this.schedule_(
        1784 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TAG_NAME),
        1785 'WebElement.getTagName()');
        1786};
        1787
        1788
        1789/**
        1790 * Schedules a command to query for the computed style of the element
        1791 * represented by this instance. If the element inherits the named style from
        1792 * its parent, the parent will be queried for its value. Where possible, color
        1793 * values will be converted to their hex representation (e.g. #00ff00 instead of
        1794 * rgb(0, 255, 0)).
        1795 * <p/>
        1796 * <em>Warning:</em> the value returned will be as the browser interprets it, so
        1797 * it may be tricky to form a proper assertion.
        1798 *
        1799 * @param {string} cssStyleProperty The name of the CSS style property to look
        1800 * up.
        1801 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1802 * requested CSS value.
        1803 */
        1804webdriver.WebElement.prototype.getCssValue = function(cssStyleProperty) {
        1805 var name = webdriver.CommandName.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;
        1806 return this.schedule_(
        1807 new webdriver.Command(name).
        1808 setParameter('propertyName', cssStyleProperty),
        1809 'WebElement.getCssValue(' + cssStyleProperty + ')');
        1810};
        1811
        1812
        1813/**
        1814 * Schedules a command to query for the value of the given attribute of the
        1815 * element. Will return the current value, even if it has been modified after
        1816 * the page has been loaded. More exactly, this method will return the value of
        1817 * the given attribute, unless that attribute is not present, in which case the
        1818 * value of the property with the same name is returned. If neither value is
        1819 * set, null is returned (for example, the "value" property of a textarea
        1820 * element). The "style" attribute is converted as best can be to a
        1821 * text representation with a trailing semi-colon. The following are deemed to
        1822 * be "boolean" attributes and will return either "true" or null:
        1823 *
        1824 * <p>async, autofocus, autoplay, checked, compact, complete, controls, declare,
        1825 * defaultchecked, defaultselected, defer, disabled, draggable, ended,
        1826 * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
        1827 * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
        1828 * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
        1829 * selected, spellcheck, truespeed, willvalidate
        1830 *
        1831 * <p>Finally, the following commonly mis-capitalized attribute/property names
        1832 * are evaluated as expected:
        1833 * <ul>
        1834 * <li>"class"
        1835 * <li>"readonly"
        1836 * </ul>
        1837 * @param {string} attributeName The name of the attribute to query.
        1838 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1839 * attribute's value. The returned value will always be either a string or
        1840 * null.
        1841 */
        1842webdriver.WebElement.prototype.getAttribute = function(attributeName) {
        1843 return this.schedule_(
        1844 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_ATTRIBUTE).
        1845 setParameter('name', attributeName),
        1846 'WebElement.getAttribute(' + attributeName + ')');
        1847};
        1848
        1849
        1850/**
        1851 * Get the visible (i.e. not hidden by CSS) innerText of this element, including
        1852 * sub-elements, without any leading or trailing whitespace.
        1853 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1854 * element's visible text.
        1855 */
        1856webdriver.WebElement.prototype.getText = function() {
        1857 return this.schedule_(
        1858 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TEXT),
        1859 'WebElement.getText()');
        1860};
        1861
        1862
        1863/**
        1864 * Schedules a command to compute the size of this element's bounding box, in
        1865 * pixels.
        1866 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1867 * element's size as a {@code {width:number, height:number}} object.
        1868 */
        1869webdriver.WebElement.prototype.getSize = function() {
        1870 return this.schedule_(
        1871 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_SIZE),
        1872 'WebElement.getSize()');
        1873};
        1874
        1875
        1876/**
        1877 * Schedules a command to compute the location of this element in page space.
        1878 * @return {!webdriver.promise.Promise} A promise that will be resolved to the
        1879 * element's location as a {@code {x:number, y:number}} object.
        1880 */
        1881webdriver.WebElement.prototype.getLocation = function() {
        1882 return this.schedule_(
        1883 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_LOCATION),
        1884 'WebElement.getLocation()');
        1885};
        1886
        1887
        1888/**
        1889 * Schedules a command to query whether the DOM element represented by this
        1890 * instance is enabled, as dicted by the {@code disabled} attribute.
        1891 * @return {!webdriver.promise.Promise} A promise that will be resolved with
        1892 * whether this element is currently enabled.
        1893 */
        1894webdriver.WebElement.prototype.isEnabled = function() {
        1895 return this.schedule_(
        1896 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_ENABLED),
        1897 'WebElement.isEnabled()');
        1898};
        1899
        1900
        1901/**
        1902 * Schedules a command to query whether this element is selected.
        1903 * @return {!webdriver.promise.Promise} A promise that will be resolved with
        1904 * whether this element is currently selected.
        1905 */
        1906webdriver.WebElement.prototype.isSelected = function() {
        1907 return this.schedule_(
        1908 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_SELECTED),
        1909 'WebElement.isSelected()');
        1910};
        1911
        1912
        1913/**
        1914 * Schedules a command to submit the form containing this element (or this
        1915 * element if it is a FORM element). This command is a no-op if the element is
        1916 * not contained in a form.
        1917 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        1918 * the form has been submitted.
        1919 */
        1920webdriver.WebElement.prototype.submit = function() {
        1921 return this.schedule_(
        1922 new webdriver.Command(webdriver.CommandName.SUBMIT_ELEMENT),
        1923 'WebElement.submit()');
        1924};
        1925
        1926
        1927/**
        1928 * Schedules a command to clear the {@code value} of this element. This command
        1929 * has no effect if the underlying DOM element is neither a text INPUT element
        1930 * nor a TEXTAREA element.
        1931 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        1932 * the element has been cleared.
        1933 */
        1934webdriver.WebElement.prototype.clear = function() {
        1935 return this.schedule_(
        1936 new webdriver.Command(webdriver.CommandName.CLEAR_ELEMENT),
        1937 'WebElement.clear()');
        1938};
        1939
        1940
        1941/**
        1942 * Schedules a command to test whether this element is currently displayed.
        1943 * @return {!webdriver.promise.Promise} A promise that will be resolved with
        1944 * whether this element is currently visible on the page.
        1945 */
        1946webdriver.WebElement.prototype.isDisplayed = function() {
        1947 return this.schedule_(
        1948 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_DISPLAYED),
        1949 'WebElement.isDisplayed()');
        1950};
        1951
        1952
        1953/**
        1954 * Schedules a command to retrieve the outer HTML of this element.
        1955 * @return {!webdriver.promise.Promise} A promise that will be resolved with
        1956 * the element's outer HTML.
        1957 */
        1958webdriver.WebElement.prototype.getOuterHtml = function() {
        1959 return this.driver_.executeScript(function() {
        1960 var element = arguments[0];
        1961 if ('outerHTML' in element) {
        1962 return element.outerHTML;
        1963 } else {
        1964 var div = element.ownerDocument.createElement('div');
        1965 div.appendChild(element.cloneNode(true));
        1966 return div.innerHTML;
        1967 }
        1968 }, this);
        1969};
        1970
        1971
        1972/**
        1973 * Schedules a command to retrieve the inner HTML of this element.
        1974 * @return {!webdriver.promise.Promise} A promise that will be resolved with the
        1975 * element's inner HTML.
        1976 */
        1977webdriver.WebElement.prototype.getInnerHtml = function() {
        1978 return this.driver_.executeScript('return arguments[0].innerHTML', this);
        1979};
        1980
        1981
        1982
        1983/**
        1984 * Represents a modal dialog such as {@code alert}, {@code confirm}, or
        1985 * {@code prompt}. Provides functions to retrieve the message displayed with
        1986 * the alert, accept or dismiss the alert, and set the response text (in the
        1987 * case of {@code prompt}).
        1988 * @param {!webdriver.WebDriver} driver The driver controlling the browser this
        1989 * alert is attached to.
        1990 * @param {!(string|webdriver.promise.Promise)} text Either the message text
        1991 * displayed with this alert, or a promise that will be resolved to said
        1992 * text.
        1993 * @constructor
        1994 * @extends {webdriver.promise.Deferred}
        1995 */
        1996webdriver.Alert = function(driver, text) {
        1997 goog.base(this, null, driver.controlFlow());
        1998
        1999 /** @private {!webdriver.WebDriver} */
        2000 this.driver_ = driver;
        2001
        2002 // This class is responsible for resolving itself; delete the resolve and
        2003 // reject methods so they may not be accessed by consumers of this class.
        2004 var fulfill = goog.partial(this.fulfill, this);
        2005 var reject = this.reject;
        2006 delete this.promise;
        2007 delete this.fulfill;
        2008 delete this.reject;
        2009
        2010 /** @private {!webdriver.promise.Promise} */
        2011 this.text_ = webdriver.promise.when(text);
        2012
        2013 // Make sure this instance is resolved when its displayed text is.
        2014 this.text_.then(fulfill, reject);
        2015};
        2016goog.inherits(webdriver.Alert, webdriver.promise.Deferred);
        2017
        2018
        2019/**
        2020 * Retrieves the message text displayed with this alert. For instance, if the
        2021 * alert were opened with alert("hello"), then this would return "hello".
        2022 * @return {!webdriver.promise.Promise} A promise that will be resolved to the
        2023 * text displayed with this alert.
        2024 */
        2025webdriver.Alert.prototype.getText = function() {
        2026 return this.text_;
        2027};
        2028
        2029
        2030/**
        2031 * Accepts this alert.
        2032 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        2033 * this command has completed.
        2034 */
        2035webdriver.Alert.prototype.accept = function() {
        2036 return this.driver_.schedule(
        2037 new webdriver.Command(webdriver.CommandName.ACCEPT_ALERT),
        2038 'WebDriver.switchTo().alert().accept()');
        2039};
        2040
        2041
        2042/**
        2043 * Dismisses this alert.
        2044 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        2045 * this command has completed.
        2046 */
        2047webdriver.Alert.prototype.dismiss = function() {
        2048 return this.driver_.schedule(
        2049 new webdriver.Command(webdriver.CommandName.DISMISS_ALERT),
        2050 'WebDriver.switchTo().alert().dismiss()');
        2051};
        2052
        2053
        2054/**
        2055 * Sets the response text on this alert. This command will return an error if
        2056 * the underlying alert does not support response text (e.g. window.alert and
        2057 * window.confirm).
        2058 * @param {string} text The text to set.
        2059 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        2060 * this command has completed.
        2061 */
        2062webdriver.Alert.prototype.sendKeys = function(text) {
        2063 return this.driver_.schedule(
        2064 new webdriver.Command(webdriver.CommandName.SET_ALERT_TEXT).
        2065 setParameter('text', text),
        2066 'WebDriver.switchTo().alert().sendKeys(' + text + ')');
        2067};
        2068
        2069
        2070
        2071/**
        2072 * An error returned to indicate that there is an unhandled modal dialog on the
        2073 * current page.
        2074 * @param {string} message The error message.
        2075 * @param {!webdriver.Alert} alert The alert handle.
        2076 * @constructor
        2077 * @extends {bot.Error}
        2078 */
        2079webdriver.UnhandledAlertError = function(message, alert) {
        2080 goog.base(this, bot.ErrorCode.MODAL_DIALOG_OPENED, message);
        2081
        2082 /** @private {!webdriver.Alert} */
        2083 this.alert_ = alert;
        2084};
        2085goog.inherits(webdriver.UnhandledAlertError, bot.Error);
        2086
        2087
        2088/**
        2089 * @return {!webdriver.Alert} The open alert.
        2090 */
        2091webdriver.UnhandledAlertError.prototype.getAlert = function() {
        2092 return this.alert_;
        2093};
        \ No newline at end of file +webdriver.js

        lib/webdriver/webdriver.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview The heart of the WebDriver JavaScript API.
        20 */
        21
        22goog.provide('webdriver.Alert');
        23goog.provide('webdriver.AlertPromise');
        24goog.provide('webdriver.FileDetector');
        25goog.provide('webdriver.UnhandledAlertError');
        26goog.provide('webdriver.WebDriver');
        27goog.provide('webdriver.WebElement');
        28goog.provide('webdriver.WebElementPromise');
        29
        30goog.require('bot.Error');
        31goog.require('bot.ErrorCode');
        32goog.require('bot.response');
        33goog.require('goog.array');
        34goog.require('goog.object');
        35goog.require('webdriver.ActionSequence');
        36goog.require('webdriver.Command');
        37goog.require('webdriver.CommandName');
        38goog.require('webdriver.Key');
        39goog.require('webdriver.Locator');
        40goog.require('webdriver.Serializable');
        41goog.require('webdriver.Session');
        42goog.require('webdriver.TouchSequence');
        43goog.require('webdriver.logging');
        44goog.require('webdriver.promise');
        45goog.require('webdriver.until');
        46
        47
        48//////////////////////////////////////////////////////////////////////////////
        49//
        50// webdriver.WebDriver
        51//
        52//////////////////////////////////////////////////////////////////////////////
        53
        54
        55
        56/**
        57 * Creates a new WebDriver client, which provides control over a browser.
        58 *
        59 * Every WebDriver command returns a {@code webdriver.promise.Promise} that
        60 * represents the result of that command. Callbacks may be registered on this
        61 * object to manipulate the command result or catch an expected error. Any
        62 * commands scheduled with a callback are considered sub-commands and will
        63 * execute before the next command in the current frame. For example:
        64 *
        65 * var message = [];
        66 * driver.call(message.push, message, 'a').then(function() {
        67 * driver.call(message.push, message, 'b');
        68 * });
        69 * driver.call(message.push, message, 'c');
        70 * driver.call(function() {
        71 * alert('message is abc? ' + (message.join('') == 'abc'));
        72 * });
        73 *
        74 * @param {!(webdriver.Session|webdriver.promise.Promise)} session Either a
        75 * known session or a promise that will be resolved to a session.
        76 * @param {!webdriver.CommandExecutor} executor The executor to use when
        77 * sending commands to the browser.
        78 * @param {webdriver.promise.ControlFlow=} opt_flow The flow to
        79 * schedule commands through. Defaults to the active flow object.
        80 * @constructor
        81 */
        82webdriver.WebDriver = function(session, executor, opt_flow) {
        83
        84 /** @private {!(webdriver.Session|webdriver.promise.Promise)} */
        85 this.session_ = session;
        86
        87 /** @private {!webdriver.CommandExecutor} */
        88 this.executor_ = executor;
        89
        90 /** @private {!webdriver.promise.ControlFlow} */
        91 this.flow_ = opt_flow || webdriver.promise.controlFlow();
        92
        93 /** @private {webdriver.FileDetector} */
        94 this.fileDetector_ = null;
        95};
        96
        97
        98/**
        99 * Creates a new WebDriver client for an existing session.
        100 * @param {!webdriver.CommandExecutor} executor Command executor to use when
        101 * querying for session details.
        102 * @param {string} sessionId ID of the session to attach to.
        103 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
        104 * commands should execute under. Defaults to the
        105 * {@link webdriver.promise.controlFlow() currently active} control flow.
        106 * @return {!webdriver.WebDriver} A new client for the specified session.
        107 */
        108webdriver.WebDriver.attachToSession = function(executor, sessionId, opt_flow) {
        109 return webdriver.WebDriver.acquireSession_(executor,
        110 new webdriver.Command(webdriver.CommandName.DESCRIBE_SESSION).
        111 setParameter('sessionId', sessionId),
        112 'WebDriver.attachToSession()',
        113 opt_flow);
        114};
        115
        116
        117/**
        118 * Creates a new WebDriver session.
        119 * @param {!webdriver.CommandExecutor} executor The executor to create the new
        120 * session with.
        121 * @param {!webdriver.Capabilities} desiredCapabilities The desired
        122 * capabilities for the new session.
        123 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
        124 * commands should execute under, including the initial session creation.
        125 * Defaults to the {@link webdriver.promise.controlFlow() currently active}
        126 * control flow.
        127 * @return {!webdriver.WebDriver} The driver for the newly created session.
        128 */
        129webdriver.WebDriver.createSession = function(
        130 executor, desiredCapabilities, opt_flow) {
        131 return webdriver.WebDriver.acquireSession_(executor,
        132 new webdriver.Command(webdriver.CommandName.NEW_SESSION).
        133 setParameter('desiredCapabilities', desiredCapabilities),
        134 'WebDriver.createSession()',
        135 opt_flow);
        136};
        137
        138
        139/**
        140 * Sends a command to the server that is expected to return the details for a
        141 * {@link webdriver.Session}. This may either be an existing session, or a
        142 * newly created one.
        143 * @param {!webdriver.CommandExecutor} executor Command executor to use when
        144 * querying for session details.
        145 * @param {!webdriver.Command} command The command to send to fetch the session
        146 * details.
        147 * @param {string} description A descriptive debug label for this action.
        148 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver
        149 * commands should execute under. Defaults to the
        150 * {@link webdriver.promise.controlFlow() currently active} control flow.
        151 * @return {!webdriver.WebDriver} A new WebDriver client for the session.
        152 * @private
        153 */
        154webdriver.WebDriver.acquireSession_ = function(
        155 executor, command, description, opt_flow) {
        156 var flow = opt_flow || webdriver.promise.controlFlow();
        157 var session = flow.execute(function() {
        158 return webdriver.WebDriver.executeCommand_(executor, command).
        159 then(function(response) {
        160 bot.response.checkResponse(response);
        161 return new webdriver.Session(response['sessionId'],
        162 response['value']);
        163 });
        164 }, description);
        165 return new webdriver.WebDriver(session, executor, flow);
        166};
        167
        168
        169/**
        170 * Converts an object to its JSON representation in the WebDriver wire protocol.
        171 * When converting values of type object, the following steps will be taken:
        172 * <ol>
        173 * <li>if the object is a WebElement, the return value will be the element's
        174 * server ID
        175 * <li>if the object is a Serializable, its
        176 * {@link webdriver.Serializable#serialize} function will be invoked and
        177 * this algorithm will recursively be applied to the result
        178 * <li>if the object provides a "toJSON" function, this algorithm will
        179 * recursively be applied to the result of that function
        180 * <li>otherwise, the value of each key will be recursively converted according
        181 * to the rules above.
        182 * </ol>
        183 *
        184 * @param {*} obj The object to convert.
        185 * @return {!webdriver.promise.Promise.<?>} A promise that will resolve to the
        186 * input value's JSON representation.
        187 * @private
        188 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
        189 */
        190webdriver.WebDriver.toWireValue_ = function(obj) {
        191 if (webdriver.promise.isPromise(obj)) {
        192 return obj.then(webdriver.WebDriver.toWireValue_);
        193 }
        194 return webdriver.promise.fulfilled(convertValue(obj));
        195
        196 function convertValue(value) {
        197 switch (goog.typeOf(value)) {
        198 case 'array':
        199 return convertKeys(value, true);
        200 case 'object':
        201 // NB: WebElement is a Serializable, but we know its serialized form
        202 // is a promise for its wire format. This is a micro optimization to
        203 // avoid creating extra promises by recursing on the promised id.
        204 if (value instanceof webdriver.WebElement) {
        205 return value.getId();
        206 }
        207 if (value instanceof webdriver.Serializable) {
        208 return webdriver.WebDriver.toWireValue_(value.serialize());
        209 }
        210 if (goog.isFunction(value.toJSON)) {
        211 return webdriver.WebDriver.toWireValue_(value.toJSON());
        212 }
        213 if (goog.isNumber(value.nodeType) && goog.isString(value.nodeName)) {
        214 throw new TypeError(
        215 'Invalid argument type: ' + value.nodeName +
        216 '(' + value.nodeType + ')');
        217 }
        218 return convertKeys(value, false);
        219 case 'function':
        220 return '' + value;
        221 case 'undefined':
        222 return null;
        223 default:
        224 return value;
        225 }
        226 }
        227
        228 function convertKeys(obj, isArray) {
        229 var numKeys = isArray ? obj.length : goog.object.getCount(obj);
        230 var ret = isArray ? new Array(numKeys) : {};
        231 if (!numKeys) {
        232 return webdriver.promise.fulfilled(ret);
        233 }
        234
        235 var numResolved = 0;
        236 var done = webdriver.promise.defer();
        237
        238 // forEach will stop iteration at undefined, where we want to convert
        239 // these to null and keep iterating.
        240 var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) {
        241 var n = arr.length;
        242 for (var i = 0; i < n; i++) {
        243 fn(arr[i], i);
        244 }
        245 };
        246
        247 forEachKey(obj, function(value, key) {
        248 if (webdriver.promise.isPromise(value)) {
        249 value.then(webdriver.WebDriver.toWireValue_).
        250 then(setValue, done.reject);
        251 } else {
        252 webdriver.promise.asap(convertValue(value), setValue, done.reject);
        253 }
        254
        255 function setValue(value) {
        256 ret[key] = value;
        257 maybeFulfill();
        258 }
        259 });
        260
        261 return done.promise;
        262
        263 function maybeFulfill() {
        264 if (++numResolved === numKeys) {
        265 done.fulfill(ret);
        266 }
        267 }
        268 }
        269};
        270
        271
        272/**
        273 * Converts a value from its JSON representation according to the WebDriver wire
        274 * protocol. Any JSON object containing a
        275 * {@code webdriver.WebElement.ELEMENT_KEY} key will be decoded to a
        276 * {@code webdriver.WebElement} object. All other values will be passed through
        277 * as is.
        278 * @param {!webdriver.WebDriver} driver The driver instance to use as the
        279 * parent of any unwrapped {@code webdriver.WebElement} values.
        280 * @param {*} value The value to convert.
        281 * @return {*} The converted value.
        282 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
        283 * @private
        284 */
        285webdriver.WebDriver.fromWireValue_ = function(driver, value) {
        286 if (goog.isArray(value)) {
        287 value = goog.array.map(/**@type {goog.array.ArrayLike}*/ (value),
        288 goog.partial(webdriver.WebDriver.fromWireValue_, driver));
        289 } else if (value && goog.isObject(value) && !goog.isFunction(value)) {
        290 if (webdriver.WebElement.ELEMENT_KEY in value) {
        291 value = new webdriver.WebElement(driver, value);
        292 } else {
        293 value = goog.object.map(/**@type {!Object}*/ (value),
        294 goog.partial(webdriver.WebDriver.fromWireValue_, driver));
        295 }
        296 }
        297 return value;
        298};
        299
        300
        301/**
        302 * Translates a command to its wire-protocol representation before passing it
        303 * to the given {@code executor} for execution.
        304 * @param {!webdriver.CommandExecutor} executor The executor to use.
        305 * @param {!webdriver.Command} command The command to execute.
        306 * @return {!webdriver.promise.Promise} A promise that will resolve with the
        307 * command response.
        308 * @private
        309 */
        310webdriver.WebDriver.executeCommand_ = function(executor, command) {
        311 return webdriver.WebDriver.toWireValue_(command.getParameters()).
        312 then(function(parameters) {
        313 command.setParameters(parameters);
        314 return webdriver.promise.checkedNodeCall(
        315 goog.bind(executor.execute, executor, command));
        316 });
        317};
        318
        319
        320/**
        321 * @return {!webdriver.promise.ControlFlow} The control flow used by this
        322 * instance.
        323 */
        324webdriver.WebDriver.prototype.controlFlow = function() {
        325 return this.flow_;
        326};
        327
        328
        329/**
        330 * Schedules a {@code webdriver.Command} to be executed by this driver's
        331 * {@code webdriver.CommandExecutor}.
        332 * @param {!webdriver.Command} command The command to schedule.
        333 * @param {string} description A description of the command for debugging.
        334 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved
        335 * with the command result.
        336 * @template T
        337 */
        338webdriver.WebDriver.prototype.schedule = function(command, description) {
        339 var self = this;
        340
        341 checkHasNotQuit();
        342 command.setParameter('sessionId', this.session_);
        343
        344 // If any of the command parameters are rejected promises, those
        345 // rejections may be reported as unhandled before the control flow
        346 // attempts to execute the command. To ensure parameters errors
        347 // propagate through the command itself, we resolve all of the
        348 // command parameters now, but suppress any errors until the ControlFlow
        349 // actually executes the command. This addresses scenarios like catching
        350 // an element not found error in:
        351 //
        352 // driver.findElement(By.id('foo')).click().thenCatch(function(e) {
        353 // if (e.code === bot.ErrorCode.NO_SUCH_ELEMENT) {
        354 // // Do something.
        355 // }
        356 // });
        357 var prepCommand = webdriver.WebDriver.toWireValue_(command.getParameters());
        358 prepCommand.thenCatch(goog.nullFunction);
        359
        360 var flow = this.flow_;
        361 var executor = this.executor_;
        362 return flow.execute(function() {
        363 // A call to WebDriver.quit() may have been scheduled in the same event
        364 // loop as this |command|, which would prevent us from detecting that the
        365 // driver has quit above. Therefore, we need to make another quick check.
        366 // We still check above so we can fail as early as possible.
        367 checkHasNotQuit();
        368
        369 // Retrieve resolved command parameters; any previously suppressed errors
        370 // will now propagate up through the control flow as part of the command
        371 // execution.
        372 return prepCommand.then(function(parameters) {
        373 command.setParameters(parameters);
        374 return webdriver.promise.checkedNodeCall(
        375 goog.bind(executor.execute, executor, command));
        376 });
        377 }, description).then(function(response) {
        378 try {
        379 bot.response.checkResponse(response);
        380 } catch (ex) {
        381 var value = response['value'];
        382 if (ex.code === bot.ErrorCode.UNEXPECTED_ALERT_OPEN) {
        383 var text = value && value['alert'] ? value['alert']['text'] : '';
        384 throw new webdriver.UnhandledAlertError(ex.message, text,
        385 new webdriver.Alert(self, text));
        386 }
        387 throw ex;
        388 }
        389 return webdriver.WebDriver.fromWireValue_(self, response['value']);
        390 });
        391
        392 function checkHasNotQuit() {
        393 if (!self.session_) {
        394 throw new Error('This driver instance does not have a valid session ID ' +
        395 '(did you call WebDriver.quit()?) and may no longer be ' +
        396 'used.');
        397 }
        398 }
        399};
        400
        401
        402/**
        403 * Sets the {@linkplain webdriver.FileDetector file detector} that should be
        404 * used with this instance.
        405 * @param {webdriver.FileDetector} detector The detector to use or {@code null}.
        406 */
        407webdriver.WebDriver.prototype.setFileDetector = function(detector) {
        408 this.fileDetector_ = detector;
        409};
        410
        411
        412// ----------------------------------------------------------------------------
        413// Client command functions:
        414// ----------------------------------------------------------------------------
        415
        416
        417/**
        418 * @return {!webdriver.promise.Promise.<!webdriver.Session>} A promise for this
        419 * client's session.
        420 */
        421webdriver.WebDriver.prototype.getSession = function() {
        422 return webdriver.promise.when(this.session_);
        423};
        424
        425
        426/**
        427 * @return {!webdriver.promise.Promise.<!webdriver.Capabilities>} A promise
        428 * that will resolve with the this instance's capabilities.
        429 */
        430webdriver.WebDriver.prototype.getCapabilities = function() {
        431 return webdriver.promise.when(this.session_, function(session) {
        432 return session.getCapabilities();
        433 });
        434};
        435
        436
        437/**
        438 * Schedules a command to quit the current session. After calling quit, this
        439 * instance will be invalidated and may no longer be used to issue commands
        440 * against the browser.
        441 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        442 * when the command has completed.
        443 */
        444webdriver.WebDriver.prototype.quit = function() {
        445 var result = this.schedule(
        446 new webdriver.Command(webdriver.CommandName.QUIT),
        447 'WebDriver.quit()');
        448 // Delete our session ID when the quit command finishes; this will allow us to
        449 // throw an error when attemnpting to use a driver post-quit.
        450 return result.thenFinally(goog.bind(function() {
        451 delete this.session_;
        452 }, this));
        453};
        454
        455
        456/**
        457 * Creates a new action sequence using this driver. The sequence will not be
        458 * scheduled for execution until {@link webdriver.ActionSequence#perform} is
        459 * called. Example:
        460 *
        461 * driver.actions().
        462 * mouseDown(element1).
        463 * mouseMove(element2).
        464 * mouseUp().
        465 * perform();
        466 *
        467 * @return {!webdriver.ActionSequence} A new action sequence for this instance.
        468 */
        469webdriver.WebDriver.prototype.actions = function() {
        470 return new webdriver.ActionSequence(this);
        471};
        472
        473
        474/**
        475 * Creates a new touch sequence using this driver. The sequence will not be
        476 * scheduled for execution until {@link webdriver.TouchSequence#perform} is
        477 * called. Example:
        478 *
        479 * driver.touchActions().
        480 * tap(element1).
        481 * doubleTap(element2).
        482 * perform();
        483 *
        484 * @return {!webdriver.TouchSequence} A new touch sequence for this instance.
        485 */
        486webdriver.WebDriver.prototype.touchActions = function() {
        487 return new webdriver.TouchSequence(this);
        488};
        489
        490
        491/**
        492 * Schedules a command to execute JavaScript in the context of the currently
        493 * selected frame or window. The script fragment will be executed as the body
        494 * of an anonymous function. If the script is provided as a function object,
        495 * that function will be converted to a string for injection into the target
        496 * window.
        497 *
        498 * Any arguments provided in addition to the script will be included as script
        499 * arguments and may be referenced using the {@code arguments} object.
        500 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}.
        501 * Arrays and objects may also be used as script arguments as long as each item
        502 * adheres to the types previously mentioned.
        503 *
        504 * The script may refer to any variables accessible from the current window.
        505 * Furthermore, the script will execute in the window's context, thus
        506 * {@code document} may be used to refer to the current document. Any local
        507 * variables will not be available once the script has finished executing,
        508 * though global variables will persist.
        509 *
        510 * If the script has a return value (i.e. if the script contains a return
        511 * statement), then the following steps will be taken for resolving this
        512 * functions return value:
        513 *
        514 * - For a HTML element, the value will resolve to a
        515 * {@link webdriver.WebElement}
        516 * - Null and undefined return values will resolve to null</li>
        517 * - Booleans, numbers, and strings will resolve as is</li>
        518 * - Functions will resolve to their string representation</li>
        519 * - For arrays and objects, each member item will be converted according to
        520 * the rules above
        521 *
        522 * @param {!(string|Function)} script The script to execute.
        523 * @param {...*} var_args The arguments to pass to the script.
        524 * @return {!webdriver.promise.Promise.<T>} A promise that will resolve to the
        525 * scripts return value.
        526 * @template T
        527 */
        528webdriver.WebDriver.prototype.executeScript = function(script, var_args) {
        529 if (goog.isFunction(script)) {
        530 script = 'return (' + script + ').apply(null, arguments);';
        531 }
        532 var args = arguments.length > 1 ? goog.array.slice(arguments, 1) : [];
        533 return this.schedule(
        534 new webdriver.Command(webdriver.CommandName.EXECUTE_SCRIPT).
        535 setParameter('script', script).
        536 setParameter('args', args),
        537 'WebDriver.executeScript()');
        538};
        539
        540
        541/**
        542 * Schedules a command to execute asynchronous JavaScript in the context of the
        543 * currently selected frame or window. The script fragment will be executed as
        544 * the body of an anonymous function. If the script is provided as a function
        545 * object, that function will be converted to a string for injection into the
        546 * target window.
        547 *
        548 * Any arguments provided in addition to the script will be included as script
        549 * arguments and may be referenced using the {@code arguments} object.
        550 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}.
        551 * Arrays and objects may also be used as script arguments as long as each item
        552 * adheres to the types previously mentioned.
        553 *
        554 * Unlike executing synchronous JavaScript with {@link #executeScript},
        555 * scripts executed with this function must explicitly signal they are finished
        556 * by invoking the provided callback. This callback will always be injected
        557 * into the executed function as the last argument, and thus may be referenced
        558 * with {@code arguments[arguments.length - 1]}. The following steps will be
        559 * taken for resolving this functions return value against the first argument
        560 * to the script's callback function:
        561 *
        562 * - For a HTML element, the value will resolve to a
        563 * {@link webdriver.WebElement}
        564 * - Null and undefined return values will resolve to null
        565 * - Booleans, numbers, and strings will resolve as is
        566 * - Functions will resolve to their string representation
        567 * - For arrays and objects, each member item will be converted according to
        568 * the rules above
        569 *
        570 * __Example #1:__ Performing a sleep that is synchronized with the currently
        571 * selected window:
        572 *
        573 * var start = new Date().getTime();
        574 * driver.executeAsyncScript(
        575 * 'window.setTimeout(arguments[arguments.length - 1], 500);').
        576 * then(function() {
        577 * console.log(
        578 * 'Elapsed time: ' + (new Date().getTime() - start) + ' ms');
        579 * });
        580 *
        581 * __Example #2:__ Synchronizing a test with an AJAX application:
        582 *
        583 * var button = driver.findElement(By.id('compose-button'));
        584 * button.click();
        585 * driver.executeAsyncScript(
        586 * 'var callback = arguments[arguments.length - 1];' +
        587 * 'mailClient.getComposeWindowWidget().onload(callback);');
        588 * driver.switchTo().frame('composeWidget');
        589 * driver.findElement(By.id('to')).sendKeys('dog@example.com');
        590 *
        591 * __Example #3:__ Injecting a XMLHttpRequest and waiting for the result. In
        592 * this example, the inject script is specified with a function literal. When
        593 * using this format, the function is converted to a string for injection, so it
        594 * should not reference any symbols not defined in the scope of the page under
        595 * test.
        596 *
        597 * driver.executeAsyncScript(function() {
        598 * var callback = arguments[arguments.length - 1];
        599 * var xhr = new XMLHttpRequest();
        600 * xhr.open("GET", "/resource/data.json", true);
        601 * xhr.onreadystatechange = function() {
        602 * if (xhr.readyState == 4) {
        603 * callback(xhr.responseText);
        604 * }
        605 * }
        606 * xhr.send('');
        607 * }).then(function(str) {
        608 * console.log(JSON.parse(str)['food']);
        609 * });
        610 *
        611 * @param {!(string|Function)} script The script to execute.
        612 * @param {...*} var_args The arguments to pass to the script.
        613 * @return {!webdriver.promise.Promise.<T>} A promise that will resolve to the
        614 * scripts return value.
        615 * @template T
        616 */
        617webdriver.WebDriver.prototype.executeAsyncScript = function(script, var_args) {
        618 if (goog.isFunction(script)) {
        619 script = 'return (' + script + ').apply(null, arguments);';
        620 }
        621 return this.schedule(
        622 new webdriver.Command(webdriver.CommandName.EXECUTE_ASYNC_SCRIPT).
        623 setParameter('script', script).
        624 setParameter('args', goog.array.slice(arguments, 1)),
        625 'WebDriver.executeScript()');
        626};
        627
        628
        629/**
        630 * Schedules a command to execute a custom function.
        631 * @param {function(...): (T|webdriver.promise.Promise.<T>)} fn The function to
        632 * execute.
        633 * @param {Object=} opt_scope The object in whose scope to execute the function.
        634 * @param {...*} var_args Any arguments to pass to the function.
        635 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved'
        636 * with the function's result.
        637 * @template T
        638 */
        639webdriver.WebDriver.prototype.call = function(fn, opt_scope, var_args) {
        640 var args = goog.array.slice(arguments, 2);
        641 var flow = this.flow_;
        642 return flow.execute(function() {
        643 return webdriver.promise.fullyResolved(args).then(function(args) {
        644 if (webdriver.promise.isGenerator(fn)) {
        645 args.unshift(fn, opt_scope);
        646 return webdriver.promise.consume.apply(null, args);
        647 }
        648 return fn.apply(opt_scope, args);
        649 });
        650 }, 'WebDriver.call(' + (fn.name || 'function') + ')');
        651};
        652
        653
        654/**
        655 * Schedules a command to wait for a condition to hold. The condition may be
        656 * specified by a {@link webdriver.until.Condition}, as a custom function, or
        657 * as a {@link webdriver.promise.Promise}.
        658 *
        659 * For a {@link webdriver.until.Condition} or function, the wait will repeatedly
        660 * evaluate the condition until it returns a truthy value. If any errors occur
        661 * while evaluating the condition, they will be allowed to propagate. In the
        662 * event a condition returns a {@link webdriver.promise.Promise promise}, the
        663 * polling loop will wait for it to be resolved and use the resolved value for
        664 * whether the condition has been satisified. Note the resolution time for
        665 * a promise is factored into whether a wait has timed out.
        666 *
        667 * *Example:* waiting up to 10 seconds for an element to be present and visible
        668 * on the page.
        669 *
        670 * var button = driver.wait(until.elementLocated(By.id('foo')), 10000);
        671 * button.click();
        672 *
        673 * This function may also be used to block the command flow on the resolution
        674 * of a {@link webdriver.promise.Promise promise}. When given a promise, the
        675 * command will simply wait for its resolution before completing. A timeout may
        676 * be provided to fail the command if the promise does not resolve before the
        677 * timeout expires.
        678 *
        679 * *Example:* Suppose you have a function, `startTestServer`, that returns a
        680 * promise for when a server is ready for requests. You can block a `WebDriver`
        681 * client on this promise with:
        682 *
        683 * var started = startTestServer();
        684 * driver.wait(started, 5 * 1000, 'Server should start within 5 seconds');
        685 * driver.get(getServerUrl());
        686 *
        687 * @param {!(webdriver.promise.Promise<T>|
        688 * webdriver.until.Condition<T>|
        689 * function(!webdriver.WebDriver): T)} condition The condition to
        690 * wait on, defined as a promise, condition object, or a function to
        691 * evaluate as a condition.
        692 * @param {number=} opt_timeout How long to wait for the condition to be true.
        693 * @param {string=} opt_message An optional message to use if the wait times
        694 * out.
        695 * @return {!webdriver.promise.Promise<T>} A promise that will be fulfilled
        696 * with the first truthy value returned by the condition function, or
        697 * rejected if the condition times out.
        698 * @template T
        699 */
        700webdriver.WebDriver.prototype.wait = function(
        701 condition, opt_timeout, opt_message) {
        702 if (webdriver.promise.isPromise(condition)) {
        703 return this.flow_.wait(
        704 /** @type {!webdriver.promise.Promise} */(condition),
        705 opt_timeout, opt_message);
        706 }
        707
        708 var message = opt_message;
        709 var fn = /** @type {!Function} */(condition);
        710 if (condition instanceof webdriver.until.Condition) {
        711 message = message || condition.description();
        712 fn = condition.fn;
        713 }
        714
        715 var driver = this;
        716 return this.flow_.wait(function() {
        717 if (webdriver.promise.isGenerator(fn)) {
        718 return webdriver.promise.consume(fn, null, [driver]);
        719 }
        720 return fn(driver);
        721 }, opt_timeout, message);
        722};
        723
        724
        725/**
        726 * Schedules a command to make the driver sleep for the given amount of time.
        727 * @param {number} ms The amount of time, in milliseconds, to sleep.
        728 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        729 * when the sleep has finished.
        730 */
        731webdriver.WebDriver.prototype.sleep = function(ms) {
        732 return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')');
        733};
        734
        735
        736/**
        737 * Schedules a command to retrieve they current window handle.
        738 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        739 * resolved with the current window handle.
        740 */
        741webdriver.WebDriver.prototype.getWindowHandle = function() {
        742 return this.schedule(
        743 new webdriver.Command(webdriver.CommandName.GET_CURRENT_WINDOW_HANDLE),
        744 'WebDriver.getWindowHandle()');
        745};
        746
        747
        748/**
        749 * Schedules a command to retrieve the current list of available window handles.
        750 * @return {!webdriver.promise.Promise.<!Array.<string>>} A promise that will
        751 * be resolved with an array of window handles.
        752 */
        753webdriver.WebDriver.prototype.getAllWindowHandles = function() {
        754 return this.schedule(
        755 new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES),
        756 'WebDriver.getAllWindowHandles()');
        757};
        758
        759
        760/**
        761 * Schedules a command to retrieve the current page's source. The page source
        762 * returned is a representation of the underlying DOM: do not expect it to be
        763 * formatted or escaped in the same way as the response sent from the web
        764 * server.
        765 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        766 * resolved with the current page source.
        767 */
        768webdriver.WebDriver.prototype.getPageSource = function() {
        769 return this.schedule(
        770 new webdriver.Command(webdriver.CommandName.GET_PAGE_SOURCE),
        771 'WebDriver.getAllWindowHandles()');
        772};
        773
        774
        775/**
        776 * Schedules a command to close the current window.
        777 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        778 * when this command has completed.
        779 */
        780webdriver.WebDriver.prototype.close = function() {
        781 return this.schedule(new webdriver.Command(webdriver.CommandName.CLOSE),
        782 'WebDriver.close()');
        783};
        784
        785
        786/**
        787 * Schedules a command to navigate to the given URL.
        788 * @param {string} url The fully qualified URL to open.
        789 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        790 * when the document has finished loading.
        791 */
        792webdriver.WebDriver.prototype.get = function(url) {
        793 return this.navigate().to(url);
        794};
        795
        796
        797/**
        798 * Schedules a command to retrieve the URL of the current page.
        799 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        800 * resolved with the current URL.
        801 */
        802webdriver.WebDriver.prototype.getCurrentUrl = function() {
        803 return this.schedule(
        804 new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL),
        805 'WebDriver.getCurrentUrl()');
        806};
        807
        808
        809/**
        810 * Schedules a command to retrieve the current page's title.
        811 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        812 * resolved with the current page's title.
        813 */
        814webdriver.WebDriver.prototype.getTitle = function() {
        815 return this.schedule(new webdriver.Command(webdriver.CommandName.GET_TITLE),
        816 'WebDriver.getTitle()');
        817};
        818
        819
        820/**
        821 * Schedule a command to find an element on the page. If the element cannot be
        822 * found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned
        823 * by the driver. Unlike other commands, this error cannot be suppressed. In
        824 * other words, scheduling a command to find an element doubles as an assert
        825 * that the element is present on the page. To test whether an element is
        826 * present on the page, use {@link #isElementPresent} instead.
        827 *
        828 * The search criteria for an element may be defined using one of the
        829 * factories in the {@link webdriver.By} namespace, or as a short-hand
        830 * {@link webdriver.By.Hash} object. For example, the following two statements
        831 * are equivalent:
        832 *
        833 * var e1 = driver.findElement(By.id('foo'));
        834 * var e2 = driver.findElement({id:'foo'});
        835 *
        836 * You may also provide a custom locator function, which takes as input
        837 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a
        838 * promise that will resolve to a WebElement. For example, to find the first
        839 * visible link on a page, you could write:
        840 *
        841 * var link = driver.findElement(firstVisibleLink);
        842 *
        843 * function firstVisibleLink(driver) {
        844 * var links = driver.findElements(By.tagName('a'));
        845 * return webdriver.promise.filter(links, function(link) {
        846 * return links.isDisplayed();
        847 * }).then(function(visibleLinks) {
        848 * return visibleLinks[0];
        849 * });
        850 * }
        851 *
        852 * When running in the browser, a WebDriver cannot manipulate DOM elements
        853 * directly; it may do so only through a {@link webdriver.WebElement} reference.
        854 * This function may be used to generate a WebElement from a DOM element. A
        855 * reference to the DOM element will be stored in a known location and this
        856 * driver will attempt to retrieve it through {@link #executeScript}. If the
        857 * element cannot be found (eg, it belongs to a different document than the
        858 * one this instance is currently focused on), a
        859 * {@link bot.ErrorCode.NO_SUCH_ELEMENT} error will be returned.
        860 *
        861 * @param {!(webdriver.Locator|webdriver.By.Hash|Element|Function)} locator The
        862 * locator to use.
        863 * @return {!webdriver.WebElement} A WebElement that can be used to issue
        864 * commands against the located element. If the element is not found, the
        865 * element will be invalidated and all scheduled commands aborted.
        866 */
        867webdriver.WebDriver.prototype.findElement = function(locator) {
        868 var id;
        869 if ('nodeType' in locator && 'ownerDocument' in locator) {
        870 var element = /** @type {!Element} */ (locator);
        871 id = this.findDomElement_(element).then(function(element) {
        872 if (!element) {
        873 throw new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT,
        874 'Unable to locate element. Is WebDriver focused on its ' +
        875 'ownerDocument\'s frame?');
        876 }
        877 return element;
        878 });
        879 } else {
        880 locator = webdriver.Locator.checkLocator(locator);
        881 if (goog.isFunction(locator)) {
        882 id = this.findElementInternal_(locator, this);
        883 } else {
        884 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENT).
        885 setParameter('using', locator.using).
        886 setParameter('value', locator.value);
        887 id = this.schedule(command, 'WebDriver.findElement(' + locator + ')');
        888 }
        889 }
        890 return new webdriver.WebElementPromise(this, id);
        891};
        892
        893
        894/**
        895 * @param {!Function} locatorFn The locator function to use.
        896 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search
        897 * context.
        898 * @return {!webdriver.promise.Promise.<!webdriver.WebElement>} A
        899 * promise that will resolve to a list of WebElements.
        900 * @private
        901 */
        902webdriver.WebDriver.prototype.findElementInternal_ = function(
        903 locatorFn, context) {
        904 return this.call(goog.partial(locatorFn, context)).then(function(result) {
        905 if (goog.isArray(result)) {
        906 result = result[0];
        907 }
        908 if (!(result instanceof webdriver.WebElement)) {
        909 throw new TypeError('Custom locator did not return a WebElement');
        910 }
        911 return result;
        912 });
        913};
        914
        915
        916/**
        917 * Locates a DOM element so that commands may be issued against it using the
        918 * {@link webdriver.WebElement} class. This is accomplished by storing a
        919 * reference to the element in an object on the element's ownerDocument.
        920 * {@link #executeScript} will then be used to create a WebElement from this
        921 * reference. This requires this driver to currently be focused on the
        922 * ownerDocument's window+frame.
        923
        924 * @param {!Element} element The element to locate.
        925 * @return {!webdriver.promise.Promise.<webdriver.WebElement>} A promise that
        926 * will be fulfilled with the located element, or null if the element
        927 * could not be found.
        928 * @private
        929 */
        930webdriver.WebDriver.prototype.findDomElement_ = function(element) {
        931 var doc = element.ownerDocument;
        932 var store = doc['$webdriver$'] = doc['$webdriver$'] || {};
        933 var id = Math.floor(Math.random() * goog.now()).toString(36);
        934 store[id] = element;
        935 element[id] = id;
        936
        937 function cleanUp() {
        938 delete store[id];
        939 }
        940
        941 function lookupElement(id) {
        942 var store = document['$webdriver$'];
        943 if (!store) {
        944 return null;
        945 }
        946
        947 var element = store[id];
        948 if (!element || element[id] !== id) {
        949 return null;
        950 }
        951 return element;
        952 }
        953
        954 /** @type {!webdriver.promise.Promise.<webdriver.WebElement>} */
        955 var foundElement = this.executeScript(lookupElement, id);
        956 foundElement.thenFinally(cleanUp);
        957 return foundElement;
        958};
        959
        960
        961/**
        962 * Schedules a command to test if an element is present on the page.
        963 *
        964 * If given a DOM element, this function will check if it belongs to the
        965 * document the driver is currently focused on. Otherwise, the function will
        966 * test if at least one element can be found with the given search criteria.
        967 *
        968 * @param {!(webdriver.Locator|webdriver.By.Hash|Element|
        969 * Function)} locatorOrElement The locator to use, or the actual
        970 * DOM element to be located by the server.
        971 * @return {!webdriver.promise.Promise.<boolean>} A promise that will resolve
        972 * with whether the element is present on the page.
        973 */
        974webdriver.WebDriver.prototype.isElementPresent = function(locatorOrElement) {
        975 if ('nodeType' in locatorOrElement && 'ownerDocument' in locatorOrElement) {
        976 return this.findDomElement_(/** @type {!Element} */ (locatorOrElement)).
        977 then(function(result) { return !!result; });
        978 } else {
        979 return this.findElements.apply(this, arguments).then(function(result) {
        980 return !!result.length;
        981 });
        982 }
        983};
        984
        985
        986/**
        987 * Schedule a command to search for multiple elements on the page.
        988 *
        989 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator
        990 * strategy to use when searching for the element.
        991 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
        992 * promise that will resolve to an array of WebElements.
        993 */
        994webdriver.WebDriver.prototype.findElements = function(locator) {
        995 locator = webdriver.Locator.checkLocator(locator);
        996 if (goog.isFunction(locator)) {
        997 return this.findElementsInternal_(locator, this);
        998 } else {
        999 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENTS).
        1000 setParameter('using', locator.using).
        1001 setParameter('value', locator.value);
        1002 return this.schedule(command, 'WebDriver.findElements(' + locator + ')');
        1003 }
        1004};
        1005
        1006
        1007/**
        1008 * @param {!Function} locatorFn The locator function to use.
        1009 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search
        1010 * context.
        1011 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
        1012 * promise that will resolve to an array of WebElements.
        1013 * @private
        1014 */
        1015webdriver.WebDriver.prototype.findElementsInternal_ = function(
        1016 locatorFn, context) {
        1017 return this.call(goog.partial(locatorFn, context)).then(function(result) {
        1018 if (result instanceof webdriver.WebElement) {
        1019 return [result];
        1020 }
        1021
        1022 if (!goog.isArray(result)) {
        1023 return [];
        1024 }
        1025
        1026 return goog.array.filter(result, function(item) {
        1027 return item instanceof webdriver.WebElement;
        1028 });
        1029 });
        1030};
        1031
        1032
        1033/**
        1034 * Schedule a command to take a screenshot. The driver makes a best effort to
        1035 * return a screenshot of the following, in order of preference:
        1036 * <ol>
        1037 * <li>Entire page
        1038 * <li>Current window
        1039 * <li>Visible portion of the current frame
        1040 * <li>The screenshot of the entire display containing the browser
        1041 * </ol>
        1042 *
        1043 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        1044 * resolved to the screenshot as a base-64 encoded PNG.
        1045 */
        1046webdriver.WebDriver.prototype.takeScreenshot = function() {
        1047 return this.schedule(new webdriver.Command(webdriver.CommandName.SCREENSHOT),
        1048 'WebDriver.takeScreenshot()');
        1049};
        1050
        1051
        1052/**
        1053 * @return {!webdriver.WebDriver.Options} The options interface for this
        1054 * instance.
        1055 */
        1056webdriver.WebDriver.prototype.manage = function() {
        1057 return new webdriver.WebDriver.Options(this);
        1058};
        1059
        1060
        1061/**
        1062 * @return {!webdriver.WebDriver.Navigation} The navigation interface for this
        1063 * instance.
        1064 */
        1065webdriver.WebDriver.prototype.navigate = function() {
        1066 return new webdriver.WebDriver.Navigation(this);
        1067};
        1068
        1069
        1070/**
        1071 * @return {!webdriver.WebDriver.TargetLocator} The target locator interface for
        1072 * this instance.
        1073 */
        1074webdriver.WebDriver.prototype.switchTo = function() {
        1075 return new webdriver.WebDriver.TargetLocator(this);
        1076};
        1077
        1078
        1079
        1080/**
        1081 * Interface for navigating back and forth in the browser history.
        1082 * @param {!webdriver.WebDriver} driver The parent driver.
        1083 * @constructor
        1084 */
        1085webdriver.WebDriver.Navigation = function(driver) {
        1086
        1087 /** @private {!webdriver.WebDriver} */
        1088 this.driver_ = driver;
        1089};
        1090
        1091
        1092/**
        1093 * Schedules a command to navigate to a new URL.
        1094 * @param {string} url The URL to navigate to.
        1095 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1096 * when the URL has been loaded.
        1097 */
        1098webdriver.WebDriver.Navigation.prototype.to = function(url) {
        1099 return this.driver_.schedule(
        1100 new webdriver.Command(webdriver.CommandName.GET).
        1101 setParameter('url', url),
        1102 'WebDriver.navigate().to(' + url + ')');
        1103};
        1104
        1105
        1106/**
        1107 * Schedules a command to move backwards in the browser history.
        1108 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1109 * when the navigation event has completed.
        1110 */
        1111webdriver.WebDriver.Navigation.prototype.back = function() {
        1112 return this.driver_.schedule(
        1113 new webdriver.Command(webdriver.CommandName.GO_BACK),
        1114 'WebDriver.navigate().back()');
        1115};
        1116
        1117
        1118/**
        1119 * Schedules a command to move forwards in the browser history.
        1120 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1121 * when the navigation event has completed.
        1122 */
        1123webdriver.WebDriver.Navigation.prototype.forward = function() {
        1124 return this.driver_.schedule(
        1125 new webdriver.Command(webdriver.CommandName.GO_FORWARD),
        1126 'WebDriver.navigate().forward()');
        1127};
        1128
        1129
        1130/**
        1131 * Schedules a command to refresh the current page.
        1132 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1133 * when the navigation event has completed.
        1134 */
        1135webdriver.WebDriver.Navigation.prototype.refresh = function() {
        1136 return this.driver_.schedule(
        1137 new webdriver.Command(webdriver.CommandName.REFRESH),
        1138 'WebDriver.navigate().refresh()');
        1139};
        1140
        1141
        1142
        1143/**
        1144 * Provides methods for managing browser and driver state.
        1145 * @param {!webdriver.WebDriver} driver The parent driver.
        1146 * @constructor
        1147 */
        1148webdriver.WebDriver.Options = function(driver) {
        1149
        1150 /** @private {!webdriver.WebDriver} */
        1151 this.driver_ = driver;
        1152};
        1153
        1154
        1155/**
        1156 * A JSON description of a browser cookie.
        1157 * @typedef {{
        1158 * name: string,
        1159 * value: string,
        1160 * path: (string|undefined),
        1161 * domain: (string|undefined),
        1162 * secure: (boolean|undefined),
        1163 * expiry: (number|undefined)
        1164 * }}
        1165 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object
        1166 */
        1167webdriver.WebDriver.Options.Cookie;
        1168
        1169
        1170/**
        1171 * Schedules a command to add a cookie.
        1172 * @param {string} name The cookie name.
        1173 * @param {string} value The cookie value.
        1174 * @param {string=} opt_path The cookie path.
        1175 * @param {string=} opt_domain The cookie domain.
        1176 * @param {boolean=} opt_isSecure Whether the cookie is secure.
        1177 * @param {(number|!Date)=} opt_expiry When the cookie expires. If specified as
        1178 * a number, should be in milliseconds since midnight, January 1, 1970 UTC.
        1179 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1180 * when the cookie has been added to the page.
        1181 */
        1182webdriver.WebDriver.Options.prototype.addCookie = function(
        1183 name, value, opt_path, opt_domain, opt_isSecure, opt_expiry) {
        1184 // We do not allow '=' or ';' in the name.
        1185 if (/[;=]/.test(name)) {
        1186 throw Error('Invalid cookie name "' + name + '"');
        1187 }
        1188
        1189 // We do not allow ';' in value.
        1190 if (/;/.test(value)) {
        1191 throw Error('Invalid cookie value "' + value + '"');
        1192 }
        1193
        1194 var cookieString = name + '=' + value +
        1195 (opt_domain ? ';domain=' + opt_domain : '') +
        1196 (opt_path ? ';path=' + opt_path : '') +
        1197 (opt_isSecure ? ';secure' : '');
        1198
        1199 var expiry;
        1200 if (goog.isDef(opt_expiry)) {
        1201 var expiryDate;
        1202 if (goog.isNumber(opt_expiry)) {
        1203 expiryDate = new Date(opt_expiry);
        1204 } else {
        1205 expiryDate = /** @type {!Date} */ (opt_expiry);
        1206 opt_expiry = expiryDate.getTime();
        1207 }
        1208 cookieString += ';expires=' + expiryDate.toUTCString();
        1209 // Convert from milliseconds to seconds.
        1210 expiry = Math.floor(/** @type {number} */ (opt_expiry) / 1000);
        1211 }
        1212
        1213 return this.driver_.schedule(
        1214 new webdriver.Command(webdriver.CommandName.ADD_COOKIE).
        1215 setParameter('cookie', {
        1216 'name': name,
        1217 'value': value,
        1218 'path': opt_path,
        1219 'domain': opt_domain,
        1220 'secure': !!opt_isSecure,
        1221 'expiry': expiry
        1222 }),
        1223 'WebDriver.manage().addCookie(' + cookieString + ')');
        1224};
        1225
        1226
        1227/**
        1228 * Schedules a command to delete all cookies visible to the current page.
        1229 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1230 * when all cookies have been deleted.
        1231 */
        1232webdriver.WebDriver.Options.prototype.deleteAllCookies = function() {
        1233 return this.driver_.schedule(
        1234 new webdriver.Command(webdriver.CommandName.DELETE_ALL_COOKIES),
        1235 'WebDriver.manage().deleteAllCookies()');
        1236};
        1237
        1238
        1239/**
        1240 * Schedules a command to delete the cookie with the given name. This command is
        1241 * a no-op if there is no cookie with the given name visible to the current
        1242 * page.
        1243 * @param {string} name The name of the cookie to delete.
        1244 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1245 * when the cookie has been deleted.
        1246 */
        1247webdriver.WebDriver.Options.prototype.deleteCookie = function(name) {
        1248 return this.driver_.schedule(
        1249 new webdriver.Command(webdriver.CommandName.DELETE_COOKIE).
        1250 setParameter('name', name),
        1251 'WebDriver.manage().deleteCookie(' + name + ')');
        1252};
        1253
        1254
        1255/**
        1256 * Schedules a command to retrieve all cookies visible to the current page.
        1257 * Each cookie will be returned as a JSON object as described by the WebDriver
        1258 * wire protocol.
        1259 * @return {!webdriver.promise.Promise.<
        1260 * !Array.<webdriver.WebDriver.Options.Cookie>>} A promise that will be
        1261 * resolved with the cookies visible to the current page.
        1262 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object
        1263 */
        1264webdriver.WebDriver.Options.prototype.getCookies = function() {
        1265 return this.driver_.schedule(
        1266 new webdriver.Command(webdriver.CommandName.GET_ALL_COOKIES),
        1267 'WebDriver.manage().getCookies()');
        1268};
        1269
        1270
        1271/**
        1272 * Schedules a command to retrieve the cookie with the given name. Returns null
        1273 * if there is no such cookie. The cookie will be returned as a JSON object as
        1274 * described by the WebDriver wire protocol.
        1275 * @param {string} name The name of the cookie to retrieve.
        1276 * @return {!webdriver.promise.Promise.<?webdriver.WebDriver.Options.Cookie>} A
        1277 * promise that will be resolved with the named cookie, or {@code null}
        1278 * if there is no such cookie.
        1279 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object
        1280 */
        1281webdriver.WebDriver.Options.prototype.getCookie = function(name) {
        1282 return this.getCookies().then(function(cookies) {
        1283 return goog.array.find(cookies, function(cookie) {
        1284 return cookie && cookie['name'] == name;
        1285 });
        1286 });
        1287};
        1288
        1289
        1290/**
        1291 * @return {!webdriver.WebDriver.Logs} The interface for managing driver
        1292 * logs.
        1293 */
        1294webdriver.WebDriver.Options.prototype.logs = function() {
        1295 return new webdriver.WebDriver.Logs(this.driver_);
        1296};
        1297
        1298
        1299/**
        1300 * @return {!webdriver.WebDriver.Timeouts} The interface for managing driver
        1301 * timeouts.
        1302 */
        1303webdriver.WebDriver.Options.prototype.timeouts = function() {
        1304 return new webdriver.WebDriver.Timeouts(this.driver_);
        1305};
        1306
        1307
        1308/**
        1309 * @return {!webdriver.WebDriver.Window} The interface for managing the
        1310 * current window.
        1311 */
        1312webdriver.WebDriver.Options.prototype.window = function() {
        1313 return new webdriver.WebDriver.Window(this.driver_);
        1314};
        1315
        1316
        1317
        1318/**
        1319 * An interface for managing timeout behavior for WebDriver instances.
        1320 * @param {!webdriver.WebDriver} driver The parent driver.
        1321 * @constructor
        1322 */
        1323webdriver.WebDriver.Timeouts = function(driver) {
        1324
        1325 /** @private {!webdriver.WebDriver} */
        1326 this.driver_ = driver;
        1327};
        1328
        1329
        1330/**
        1331 * Specifies the amount of time the driver should wait when searching for an
        1332 * element if it is not immediately present.
        1333 *
        1334 * When searching for a single element, the driver should poll the page
        1335 * until the element has been found, or this timeout expires before failing
        1336 * with a {@link bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching
        1337 * for multiple elements, the driver should poll the page until at least one
        1338 * element has been found or this timeout has expired.
        1339 *
        1340 * Setting the wait timeout to 0 (its default value), disables implicit
        1341 * waiting.
        1342 *
        1343 * Increasing the implicit wait timeout should be used judiciously as it
        1344 * will have an adverse effect on test run time, especially when used with
        1345 * slower location strategies like XPath.
        1346 *
        1347 * @param {number} ms The amount of time to wait, in milliseconds.
        1348 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1349 * when the implicit wait timeout has been set.
        1350 */
        1351webdriver.WebDriver.Timeouts.prototype.implicitlyWait = function(ms) {
        1352 return this.driver_.schedule(
        1353 new webdriver.Command(webdriver.CommandName.IMPLICITLY_WAIT).
        1354 setParameter('ms', ms < 0 ? 0 : ms),
        1355 'WebDriver.manage().timeouts().implicitlyWait(' + ms + ')');
        1356};
        1357
        1358
        1359/**
        1360 * Sets the amount of time to wait, in milliseconds, for an asynchronous script
        1361 * to finish execution before returning an error. If the timeout is less than or
        1362 * equal to 0, the script will be allowed to run indefinitely.
        1363 *
        1364 * @param {number} ms The amount of time to wait, in milliseconds.
        1365 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1366 * when the script timeout has been set.
        1367 */
        1368webdriver.WebDriver.Timeouts.prototype.setScriptTimeout = function(ms) {
        1369 return this.driver_.schedule(
        1370 new webdriver.Command(webdriver.CommandName.SET_SCRIPT_TIMEOUT).
        1371 setParameter('ms', ms < 0 ? 0 : ms),
        1372 'WebDriver.manage().timeouts().setScriptTimeout(' + ms + ')');
        1373};
        1374
        1375
        1376/**
        1377 * Sets the amount of time to wait for a page load to complete before returning
        1378 * an error. If the timeout is negative, page loads may be indefinite.
        1379 * @param {number} ms The amount of time to wait, in milliseconds.
        1380 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1381 * when the timeout has been set.
        1382 */
        1383webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout = function(ms) {
        1384 return this.driver_.schedule(
        1385 new webdriver.Command(webdriver.CommandName.SET_TIMEOUT).
        1386 setParameter('type', 'page load').
        1387 setParameter('ms', ms),
        1388 'WebDriver.manage().timeouts().pageLoadTimeout(' + ms + ')');
        1389};
        1390
        1391
        1392
        1393/**
        1394 * An interface for managing the current window.
        1395 * @param {!webdriver.WebDriver} driver The parent driver.
        1396 * @constructor
        1397 */
        1398webdriver.WebDriver.Window = function(driver) {
        1399
        1400 /** @private {!webdriver.WebDriver} */
        1401 this.driver_ = driver;
        1402};
        1403
        1404
        1405/**
        1406 * Retrieves the window's current position, relative to the top left corner of
        1407 * the screen.
        1408 * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that
        1409 * will be resolved with the window's position in the form of a
        1410 * {x:number, y:number} object literal.
        1411 */
        1412webdriver.WebDriver.Window.prototype.getPosition = function() {
        1413 return this.driver_.schedule(
        1414 new webdriver.Command(webdriver.CommandName.GET_WINDOW_POSITION).
        1415 setParameter('windowHandle', 'current'),
        1416 'WebDriver.manage().window().getPosition()');
        1417};
        1418
        1419
        1420/**
        1421 * Repositions the current window.
        1422 * @param {number} x The desired horizontal position, relative to the left side
        1423 * of the screen.
        1424 * @param {number} y The desired vertical position, relative to the top of the
        1425 * of the screen.
        1426 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1427 * when the command has completed.
        1428 */
        1429webdriver.WebDriver.Window.prototype.setPosition = function(x, y) {
        1430 return this.driver_.schedule(
        1431 new webdriver.Command(webdriver.CommandName.SET_WINDOW_POSITION).
        1432 setParameter('windowHandle', 'current').
        1433 setParameter('x', x).
        1434 setParameter('y', y),
        1435 'WebDriver.manage().window().setPosition(' + x + ', ' + y + ')');
        1436};
        1437
        1438
        1439/**
        1440 * Retrieves the window's current size.
        1441 * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A
        1442 * promise that will be resolved with the window's size in the form of a
        1443 * {width:number, height:number} object literal.
        1444 */
        1445webdriver.WebDriver.Window.prototype.getSize = function() {
        1446 return this.driver_.schedule(
        1447 new webdriver.Command(webdriver.CommandName.GET_WINDOW_SIZE).
        1448 setParameter('windowHandle', 'current'),
        1449 'WebDriver.manage().window().getSize()');
        1450};
        1451
        1452
        1453/**
        1454 * Resizes the current window.
        1455 * @param {number} width The desired window width.
        1456 * @param {number} height The desired window height.
        1457 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1458 * when the command has completed.
        1459 */
        1460webdriver.WebDriver.Window.prototype.setSize = function(width, height) {
        1461 return this.driver_.schedule(
        1462 new webdriver.Command(webdriver.CommandName.SET_WINDOW_SIZE).
        1463 setParameter('windowHandle', 'current').
        1464 setParameter('width', width).
        1465 setParameter('height', height),
        1466 'WebDriver.manage().window().setSize(' + width + ', ' + height + ')');
        1467};
        1468
        1469
        1470/**
        1471 * Maximizes the current window.
        1472 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1473 * when the command has completed.
        1474 */
        1475webdriver.WebDriver.Window.prototype.maximize = function() {
        1476 return this.driver_.schedule(
        1477 new webdriver.Command(webdriver.CommandName.MAXIMIZE_WINDOW).
        1478 setParameter('windowHandle', 'current'),
        1479 'WebDriver.manage().window().maximize()');
        1480};
        1481
        1482
        1483/**
        1484 * Interface for managing WebDriver log records.
        1485 * @param {!webdriver.WebDriver} driver The parent driver.
        1486 * @constructor
        1487 */
        1488webdriver.WebDriver.Logs = function(driver) {
        1489
        1490 /** @private {!webdriver.WebDriver} */
        1491 this.driver_ = driver;
        1492};
        1493
        1494
        1495/**
        1496 * Fetches available log entries for the given type.
        1497 *
        1498 * Note that log buffers are reset after each call, meaning that available
        1499 * log entries correspond to those entries not yet returned for a given log
        1500 * type. In practice, this means that this call will return the available log
        1501 * entries since the last call, or from the start of the session.
        1502 *
        1503 * @param {!webdriver.logging.Type} type The desired log type.
        1504 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Entry>>} A
        1505 * promise that will resolve to a list of log entries for the specified
        1506 * type.
        1507 */
        1508webdriver.WebDriver.Logs.prototype.get = function(type) {
        1509 return this.driver_.schedule(
        1510 new webdriver.Command(webdriver.CommandName.GET_LOG).
        1511 setParameter('type', type),
        1512 'WebDriver.manage().logs().get(' + type + ')').
        1513 then(function(entries) {
        1514 return goog.array.map(entries, function(entry) {
        1515 if (!(entry instanceof webdriver.logging.Entry)) {
        1516 return new webdriver.logging.Entry(
        1517 entry['level'], entry['message'], entry['timestamp'],
        1518 entry['type']);
        1519 }
        1520 return entry;
        1521 });
        1522 });
        1523};
        1524
        1525
        1526/**
        1527 * Retrieves the log types available to this driver.
        1528 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Type>>} A
        1529 * promise that will resolve to a list of available log types.
        1530 */
        1531webdriver.WebDriver.Logs.prototype.getAvailableLogTypes = function() {
        1532 return this.driver_.schedule(
        1533 new webdriver.Command(webdriver.CommandName.GET_AVAILABLE_LOG_TYPES),
        1534 'WebDriver.manage().logs().getAvailableLogTypes()');
        1535};
        1536
        1537
        1538
        1539/**
        1540 * An interface for changing the focus of the driver to another frame or window.
        1541 * @param {!webdriver.WebDriver} driver The parent driver.
        1542 * @constructor
        1543 */
        1544webdriver.WebDriver.TargetLocator = function(driver) {
        1545
        1546 /** @private {!webdriver.WebDriver} */
        1547 this.driver_ = driver;
        1548};
        1549
        1550
        1551/**
        1552 * Schedules a command retrieve the {@code document.activeElement} element on
        1553 * the current document, or {@code document.body} if activeElement is not
        1554 * available.
        1555 * @return {!webdriver.WebElementPromise} The active element.
        1556 */
        1557webdriver.WebDriver.TargetLocator.prototype.activeElement = function() {
        1558 var id = this.driver_.schedule(
        1559 new webdriver.Command(webdriver.CommandName.GET_ACTIVE_ELEMENT),
        1560 'WebDriver.switchTo().activeElement()');
        1561 return new webdriver.WebElementPromise(this.driver_, id);
        1562};
        1563
        1564
        1565/**
        1566 * Schedules a command to switch focus of all future commands to the first frame
        1567 * on the page.
        1568 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1569 * when the driver has changed focus to the default content.
        1570 */
        1571webdriver.WebDriver.TargetLocator.prototype.defaultContent = function() {
        1572 return this.driver_.schedule(
        1573 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME).
        1574 setParameter('id', null),
        1575 'WebDriver.switchTo().defaultContent()');
        1576};
        1577
        1578
        1579/**
        1580 * Schedules a command to switch the focus of all future commands to another
        1581 * frame on the page.
        1582 *
        1583 * If the frame is specified by a number, the command will switch to the frame
        1584 * by its (zero-based) index into
        1585 * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames).
        1586 *
        1587 * If the frame is specified by a string, the command will select the frame by
        1588 * its name or ID. To select sub-frames, simply separate the frame names/IDs by
        1589 * dots. As an example, "main.child" will select the frame with the name "main"
        1590 * and then its child "child".
        1591 *
        1592 * If the specified frame can not be found, the deferred result will errback
        1593 * with a {@link bot.ErrorCode.NO_SUCH_FRAME} error.
        1594 *
        1595 * @param {string|number} nameOrIndex The frame locator.
        1596 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1597 * when the driver has changed focus to the specified frame.
        1598 */
        1599webdriver.WebDriver.TargetLocator.prototype.frame = function(nameOrIndex) {
        1600 return this.driver_.schedule(
        1601 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME).
        1602 setParameter('id', nameOrIndex),
        1603 'WebDriver.switchTo().frame(' + nameOrIndex + ')');
        1604};
        1605
        1606
        1607/**
        1608 * Schedules a command to switch the focus of all future commands to another
        1609 * window. Windows may be specified by their {@code window.name} attribute or
        1610 * by its handle (as returned by {@link webdriver.WebDriver#getWindowHandles}).
        1611 *
        1612 * If the specificed window can not be found, the deferred result will errback
        1613 * with a {@link bot.ErrorCode.NO_SUCH_WINDOW} error.
        1614 *
        1615 * @param {string} nameOrHandle The name or window handle of the window to
        1616 * switch focus to.
        1617 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1618 * when the driver has changed focus to the specified window.
        1619 */
        1620webdriver.WebDriver.TargetLocator.prototype.window = function(nameOrHandle) {
        1621 return this.driver_.schedule(
        1622 new webdriver.Command(webdriver.CommandName.SWITCH_TO_WINDOW).
        1623 setParameter('name', nameOrHandle),
        1624 'WebDriver.switchTo().window(' + nameOrHandle + ')');
        1625};
        1626
        1627
        1628/**
        1629 * Schedules a command to change focus to the active alert dialog. This command
        1630 * will return a {@link bot.ErrorCode.NO_SUCH_ALERT} error if an alert dialog
        1631 * is not currently open.
        1632 * @return {!webdriver.AlertPromise} The open alert.
        1633 */
        1634webdriver.WebDriver.TargetLocator.prototype.alert = function() {
        1635 var text = this.driver_.schedule(
        1636 new webdriver.Command(webdriver.CommandName.GET_ALERT_TEXT),
        1637 'WebDriver.switchTo().alert()');
        1638 var driver = this.driver_;
        1639 return new webdriver.AlertPromise(driver, text.then(function(text) {
        1640 return new webdriver.Alert(driver, text);
        1641 }));
        1642};
        1643
        1644
        1645/**
        1646 * Simulate pressing many keys at once in a "chord". Takes a sequence of
        1647 * {@link webdriver.Key}s or strings, appends each of the values to a string,
        1648 * and adds the chord termination key ({@link webdriver.Key.NULL}) and returns
        1649 * the resultant string.
        1650 *
        1651 * Note: when the low-level webdriver key handlers see Keys.NULL, active
        1652 * modifier keys (CTRL/ALT/SHIFT/etc) release via a keyup event.
        1653 *
        1654 * @param {...string} var_args The key sequence to concatenate.
        1655 * @return {string} The null-terminated key sequence.
        1656 * @see http://code.google.com/p/webdriver/issues/detail?id=79
        1657 */
        1658webdriver.Key.chord = function(var_args) {
        1659 var sequence = goog.array.reduce(
        1660 goog.array.slice(arguments, 0),
        1661 function(str, key) {
        1662 return str + key;
        1663 }, '');
        1664 sequence += webdriver.Key.NULL;
        1665 return sequence;
        1666};
        1667
        1668
        1669//////////////////////////////////////////////////////////////////////////////
        1670//
        1671// webdriver.WebElement
        1672//
        1673//////////////////////////////////////////////////////////////////////////////
        1674
        1675
        1676
        1677/**
        1678 * Represents a DOM element. WebElements can be found by searching from the
        1679 * document root using a {@link webdriver.WebDriver} instance, or by searching
        1680 * under another WebElement:
        1681 *
        1682 * driver.get('http://www.google.com');
        1683 * var searchForm = driver.findElement(By.tagName('form'));
        1684 * var searchBox = searchForm.findElement(By.name('q'));
        1685 * searchBox.sendKeys('webdriver');
        1686 *
        1687 * The WebElement is implemented as a promise for compatibility with the promise
        1688 * API. It will always resolve itself when its internal state has been fully
        1689 * resolved and commands may be issued against the element. This can be used to
        1690 * catch errors when an element cannot be located on the page:
        1691 *
        1692 * driver.findElement(By.id('not-there')).then(function(element) {
        1693 * alert('Found an element that was not expected to be there!');
        1694 * }, function(error) {
        1695 * alert('The element was not found, as expected');
        1696 * });
        1697 *
        1698 * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this
        1699 * element.
        1700 * @param {!(webdriver.promise.Promise.<webdriver.WebElement.Id>|
        1701 * webdriver.WebElement.Id)} id The server-assigned opaque ID for the
        1702 * underlying DOM element.
        1703 * @constructor
        1704 * @extends {webdriver.Serializable.<webdriver.WebElement.Id>}
        1705 */
        1706webdriver.WebElement = function(driver, id) {
        1707 webdriver.Serializable.call(this);
        1708
        1709 /** @private {!webdriver.WebDriver} */
        1710 this.driver_ = driver;
        1711
        1712 /** @private {!webdriver.promise.Promise.<webdriver.WebElement.Id>} */
        1713 this.id_ = id instanceof webdriver.promise.Promise ?
        1714 id : webdriver.promise.fulfilled(id);
        1715};
        1716goog.inherits(webdriver.WebElement, webdriver.Serializable);
        1717
        1718
        1719/**
        1720 * Wire protocol definition of a WebElement ID.
        1721 * @typedef {{ELEMENT: string}}
        1722 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
        1723 */
        1724webdriver.WebElement.Id;
        1725
        1726
        1727/**
        1728 * The property key used in the wire protocol to indicate that a JSON object
        1729 * contains the ID of a WebElement.
        1730 * @type {string}
        1731 * @const
        1732 */
        1733webdriver.WebElement.ELEMENT_KEY = 'ELEMENT';
        1734
        1735
        1736/**
        1737 * Compares to WebElements for equality.
        1738 * @param {!webdriver.WebElement} a A WebElement.
        1739 * @param {!webdriver.WebElement} b A WebElement.
        1740 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
        1741 * resolved to whether the two WebElements are equal.
        1742 */
        1743webdriver.WebElement.equals = function(a, b) {
        1744 if (a == b) {
        1745 return webdriver.promise.fulfilled(true);
        1746 }
        1747 var ids = [a.getId(), b.getId()];
        1748 return webdriver.promise.all(ids).then(function(ids) {
        1749 // If the two element's have the same ID, they should be considered
        1750 // equal. Otherwise, they may still be equivalent, but we'll need to
        1751 // ask the server to check for us.
        1752 if (ids[0][webdriver.WebElement.ELEMENT_KEY] ==
        1753 ids[1][webdriver.WebElement.ELEMENT_KEY]) {
        1754 return true;
        1755 }
        1756
        1757 var command = new webdriver.Command(webdriver.CommandName.ELEMENT_EQUALS);
        1758 command.setParameter('id', ids[0]);
        1759 command.setParameter('other', ids[1]);
        1760 return a.driver_.schedule(command, 'webdriver.WebElement.equals()');
        1761 });
        1762};
        1763
        1764
        1765/**
        1766 * @return {!webdriver.WebDriver} The parent driver for this instance.
        1767 */
        1768webdriver.WebElement.prototype.getDriver = function() {
        1769 return this.driver_;
        1770};
        1771
        1772
        1773/**
        1774 * @return {!webdriver.promise.Promise.<webdriver.WebElement.Id>} A promise
        1775 * that resolves to this element's JSON representation as defined by the
        1776 * WebDriver wire protocol.
        1777 * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
        1778 */
        1779webdriver.WebElement.prototype.getId = function() {
        1780 return this.id_;
        1781};
        1782
        1783
        1784/**
        1785 * Returns the raw ID string ID for this element.
        1786 * @return {!webdriver.promise.Promise<string>} A promise that resolves to this
        1787 * element's raw ID as a string value.
        1788 * @package
        1789 */
        1790webdriver.WebElement.prototype.getRawId = function() {
        1791 return this.getId().then(function(value) {
        1792 return value['ELEMENT'];
        1793 });
        1794};
        1795
        1796
        1797/** @override */
        1798webdriver.WebElement.prototype.serialize = function() {
        1799 return this.getId();
        1800};
        1801
        1802
        1803/**
        1804 * Schedules a command that targets this element with the parent WebDriver
        1805 * instance. Will ensure this element's ID is included in the command parameters
        1806 * under the "id" key.
        1807 * @param {!webdriver.Command} command The command to schedule.
        1808 * @param {string} description A description of the command for debugging.
        1809 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved
        1810 * with the command result.
        1811 * @template T
        1812 * @see webdriver.WebDriver.prototype.schedule
        1813 * @private
        1814 */
        1815webdriver.WebElement.prototype.schedule_ = function(command, description) {
        1816 command.setParameter('id', this.getId());
        1817 return this.driver_.schedule(command, description);
        1818};
        1819
        1820
        1821/**
        1822 * Schedule a command to find a descendant of this element. If the element
        1823 * cannot be found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will
        1824 * be returned by the driver. Unlike other commands, this error cannot be
        1825 * suppressed. In other words, scheduling a command to find an element doubles
        1826 * as an assert that the element is present on the page. To test whether an
        1827 * element is present on the page, use {@link #isElementPresent} instead.
        1828 *
        1829 * The search criteria for an element may be defined using one of the
        1830 * factories in the {@link webdriver.By} namespace, or as a short-hand
        1831 * {@link webdriver.By.Hash} object. For example, the following two statements
        1832 * are equivalent:
        1833 *
        1834 * var e1 = element.findElement(By.id('foo'));
        1835 * var e2 = element.findElement({id:'foo'});
        1836 *
        1837 * You may also provide a custom locator function, which takes as input
        1838 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a
        1839 * promise that will resolve to a WebElement. For example, to find the first
        1840 * visible link on a page, you could write:
        1841 *
        1842 * var link = element.findElement(firstVisibleLink);
        1843 *
        1844 * function firstVisibleLink(element) {
        1845 * var links = element.findElements(By.tagName('a'));
        1846 * return webdriver.promise.filter(links, function(link) {
        1847 * return links.isDisplayed();
        1848 * }).then(function(visibleLinks) {
        1849 * return visibleLinks[0];
        1850 * });
        1851 * }
        1852 *
        1853 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
        1854 * locator strategy to use when searching for the element.
        1855 * @return {!webdriver.WebElement} A WebElement that can be used to issue
        1856 * commands against the located element. If the element is not found, the
        1857 * element will be invalidated and all scheduled commands aborted.
        1858 */
        1859webdriver.WebElement.prototype.findElement = function(locator) {
        1860 locator = webdriver.Locator.checkLocator(locator);
        1861 var id;
        1862 if (goog.isFunction(locator)) {
        1863 id = this.driver_.findElementInternal_(locator, this);
        1864 } else {
        1865 var command = new webdriver.Command(
        1866 webdriver.CommandName.FIND_CHILD_ELEMENT).
        1867 setParameter('using', locator.using).
        1868 setParameter('value', locator.value);
        1869 id = this.schedule_(command, 'WebElement.findElement(' + locator + ')');
        1870 }
        1871 return new webdriver.WebElementPromise(this.driver_, id);
        1872};
        1873
        1874
        1875/**
        1876 * Schedules a command to test if there is at least one descendant of this
        1877 * element that matches the given search criteria.
        1878 *
        1879 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
        1880 * locator strategy to use when searching for the element.
        1881 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
        1882 * resolved with whether an element could be located on the page.
        1883 */
        1884webdriver.WebElement.prototype.isElementPresent = function(locator) {
        1885 return this.findElements(locator).then(function(result) {
        1886 return !!result.length;
        1887 });
        1888};
        1889
        1890
        1891/**
        1892 * Schedules a command to find all of the descendants of this element that
        1893 * match the given search criteria.
        1894 *
        1895 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The
        1896 * locator strategy to use when searching for the elements.
        1897 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A
        1898 * promise that will resolve to an array of WebElements.
        1899 */
        1900webdriver.WebElement.prototype.findElements = function(locator) {
        1901 locator = webdriver.Locator.checkLocator(locator);
        1902 if (goog.isFunction(locator)) {
        1903 return this.driver_.findElementsInternal_(locator, this);
        1904 } else {
        1905 var command = new webdriver.Command(
        1906 webdriver.CommandName.FIND_CHILD_ELEMENTS).
        1907 setParameter('using', locator.using).
        1908 setParameter('value', locator.value);
        1909 return this.schedule_(command, 'WebElement.findElements(' + locator + ')');
        1910 }
        1911};
        1912
        1913
        1914/**
        1915 * Schedules a command to click on this element.
        1916 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1917 * when the click command has completed.
        1918 */
        1919webdriver.WebElement.prototype.click = function() {
        1920 return this.schedule_(
        1921 new webdriver.Command(webdriver.CommandName.CLICK_ELEMENT),
        1922 'WebElement.click()');
        1923};
        1924
        1925
        1926/**
        1927 * Schedules a command to type a sequence on the DOM element represented by this
        1928 * instance.
        1929 *
        1930 * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
        1931 * processed in the keysequence, that key state is toggled until one of the
        1932 * following occurs:
        1933 *
        1934 * - The modifier key is encountered again in the sequence. At this point the
        1935 * state of the key is toggled (along with the appropriate keyup/down events).
        1936 * - The {@link webdriver.Key.NULL} key is encountered in the sequence. When
        1937 * this key is encountered, all modifier keys current in the down state are
        1938 * released (with accompanying keyup events). The NULL key can be used to
        1939 * simulate common keyboard shortcuts:
        1940 *
        1941 * element.sendKeys("text was",
        1942 * webdriver.Key.CONTROL, "a", webdriver.Key.NULL,
        1943 * "now text is");
        1944 * // Alternatively:
        1945 * element.sendKeys("text was",
        1946 * webdriver.Key.chord(webdriver.Key.CONTROL, "a"),
        1947 * "now text is");
        1948 *
        1949 * - The end of the keysequence is encountered. When there are no more keys
        1950 * to type, all depressed modifier keys are released (with accompanying keyup
        1951 * events).
        1952 *
        1953 * If this element is a file input ({@code <input type="file">}), the
        1954 * specified key sequence should specify the path to the file to attach to
        1955 * the element. This is analgous to the user clicking "Browse..." and entering
        1956 * the path into the file select dialog.
        1957 *
        1958 * var form = driver.findElement(By.css('form'));
        1959 * var element = form.findElement(By.css('input[type=file]'));
        1960 * element.sendKeys('/path/to/file.txt');
        1961 * form.submit();
        1962 *
        1963 * For uploads to function correctly, the entered path must reference a file
        1964 * on the _browser's_ machine, not the local machine running this script. When
        1965 * running against a remote Selenium server, a {@link webdriver.FileDetector}
        1966 * may be used to transparently copy files to the remote machine before
        1967 * attempting to upload them in the browser.
        1968 *
        1969 * __Note:__ On browsers where native keyboard events are not supported
        1970 * (e.g. Firefox on OS X), key events will be synthesized. Special
        1971 * punctionation keys will be synthesized according to a standard QWERTY en-us
        1972 * keyboard layout.
        1973 *
        1974 * @param {...(string|!webdriver.promise.Promise<string>)} var_args The sequence
        1975 * of keys to type. All arguments will be joined into a single sequence.
        1976 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        1977 * when all keys have been typed.
        1978 */
        1979webdriver.WebElement.prototype.sendKeys = function(var_args) {
        1980 // Coerce every argument to a string. This protects us from users that
        1981 // ignore the jsdoc and give us a number (which ends up causing problems on
        1982 // the server, which requires strings).
        1983 var keys = webdriver.promise.all(goog.array.slice(arguments, 0)).
        1984 then(function(keys) {
        1985 return goog.array.map(keys, String);
        1986 });
        1987 if (!this.driver_.fileDetector_) {
        1988 return this.schedule_(
        1989 new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT).
        1990 setParameter('value', keys),
        1991 'WebElement.sendKeys()');
        1992 }
        1993
        1994 // Suppress unhandled rejection errors until the flow executes the command.
        1995 keys.thenCatch(goog.nullFunction);
        1996
        1997 var element = this;
        1998 return this.driver_.flow_.execute(function() {
        1999 return keys.then(function(keys) {
        2000 return element.driver_.fileDetector_
        2001 .handleFile(element.driver_, keys.join(''));
        2002 }).then(function(keys) {
        2003 return element.schedule_(
        2004 new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT).
        2005 setParameter('value', [keys]),
        2006 'WebElement.sendKeys()');
        2007 });
        2008 }, 'WebElement.sendKeys()');
        2009};
        2010
        2011
        2012/**
        2013 * Schedules a command to query for the tag/node name of this element.
        2014 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        2015 * resolved with the element's tag name.
        2016 */
        2017webdriver.WebElement.prototype.getTagName = function() {
        2018 return this.schedule_(
        2019 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TAG_NAME),
        2020 'WebElement.getTagName()');
        2021};
        2022
        2023
        2024/**
        2025 * Schedules a command to query for the computed style of the element
        2026 * represented by this instance. If the element inherits the named style from
        2027 * its parent, the parent will be queried for its value. Where possible, color
        2028 * values will be converted to their hex representation (e.g. #00ff00 instead of
        2029 * rgb(0, 255, 0)).
        2030 *
        2031 * _Warning:_ the value returned will be as the browser interprets it, so
        2032 * it may be tricky to form a proper assertion.
        2033 *
        2034 * @param {string} cssStyleProperty The name of the CSS style property to look
        2035 * up.
        2036 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        2037 * resolved with the requested CSS value.
        2038 */
        2039webdriver.WebElement.prototype.getCssValue = function(cssStyleProperty) {
        2040 var name = webdriver.CommandName.GET_ELEMENT_VALUE_OF_CSS_PROPERTY;
        2041 return this.schedule_(
        2042 new webdriver.Command(name).
        2043 setParameter('propertyName', cssStyleProperty),
        2044 'WebElement.getCssValue(' + cssStyleProperty + ')');
        2045};
        2046
        2047
        2048/**
        2049 * Schedules a command to query for the value of the given attribute of the
        2050 * element. Will return the current value, even if it has been modified after
        2051 * the page has been loaded. More exactly, this method will return the value of
        2052 * the given attribute, unless that attribute is not present, in which case the
        2053 * value of the property with the same name is returned. If neither value is
        2054 * set, null is returned (for example, the "value" property of a textarea
        2055 * element). The "style" attribute is converted as best can be to a
        2056 * text representation with a trailing semi-colon. The following are deemed to
        2057 * be "boolean" attributes and will return either "true" or null:
        2058 *
        2059 * async, autofocus, autoplay, checked, compact, complete, controls, declare,
        2060 * defaultchecked, defaultselected, defer, disabled, draggable, ended,
        2061 * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
        2062 * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
        2063 * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
        2064 * selected, spellcheck, truespeed, willvalidate
        2065 *
        2066 * Finally, the following commonly mis-capitalized attribute/property names
        2067 * are evaluated as expected:
        2068 *
        2069 * - "class"
        2070 * - "readonly"
        2071 *
        2072 * @param {string} attributeName The name of the attribute to query.
        2073 * @return {!webdriver.promise.Promise.<?string>} A promise that will be
        2074 * resolved with the attribute's value. The returned value will always be
        2075 * either a string or null.
        2076 */
        2077webdriver.WebElement.prototype.getAttribute = function(attributeName) {
        2078 return this.schedule_(
        2079 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_ATTRIBUTE).
        2080 setParameter('name', attributeName),
        2081 'WebElement.getAttribute(' + attributeName + ')');
        2082};
        2083
        2084
        2085/**
        2086 * Get the visible (i.e. not hidden by CSS) innerText of this element, including
        2087 * sub-elements, without any leading or trailing whitespace.
        2088 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        2089 * resolved with the element's visible text.
        2090 */
        2091webdriver.WebElement.prototype.getText = function() {
        2092 return this.schedule_(
        2093 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TEXT),
        2094 'WebElement.getText()');
        2095};
        2096
        2097
        2098/**
        2099 * Schedules a command to compute the size of this element's bounding box, in
        2100 * pixels.
        2101 * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A
        2102 * promise that will be resolved with the element's size as a
        2103 * {@code {width:number, height:number}} object.
        2104 */
        2105webdriver.WebElement.prototype.getSize = function() {
        2106 return this.schedule_(
        2107 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_SIZE),
        2108 'WebElement.getSize()');
        2109};
        2110
        2111
        2112/**
        2113 * Schedules a command to compute the location of this element in page space.
        2114 * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that
        2115 * will be resolved to the element's location as a
        2116 * {@code {x:number, y:number}} object.
        2117 */
        2118webdriver.WebElement.prototype.getLocation = function() {
        2119 return this.schedule_(
        2120 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_LOCATION),
        2121 'WebElement.getLocation()');
        2122};
        2123
        2124
        2125/**
        2126 * Schedules a command to query whether the DOM element represented by this
        2127 * instance is enabled, as dicted by the {@code disabled} attribute.
        2128 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
        2129 * resolved with whether this element is currently enabled.
        2130 */
        2131webdriver.WebElement.prototype.isEnabled = function() {
        2132 return this.schedule_(
        2133 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_ENABLED),
        2134 'WebElement.isEnabled()');
        2135};
        2136
        2137
        2138/**
        2139 * Schedules a command to query whether this element is selected.
        2140 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
        2141 * resolved with whether this element is currently selected.
        2142 */
        2143webdriver.WebElement.prototype.isSelected = function() {
        2144 return this.schedule_(
        2145 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_SELECTED),
        2146 'WebElement.isSelected()');
        2147};
        2148
        2149
        2150/**
        2151 * Schedules a command to submit the form containing this element (or this
        2152 * element if it is a FORM element). This command is a no-op if the element is
        2153 * not contained in a form.
        2154 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        2155 * when the form has been submitted.
        2156 */
        2157webdriver.WebElement.prototype.submit = function() {
        2158 return this.schedule_(
        2159 new webdriver.Command(webdriver.CommandName.SUBMIT_ELEMENT),
        2160 'WebElement.submit()');
        2161};
        2162
        2163
        2164/**
        2165 * Schedules a command to clear the {@code value} of this element. This command
        2166 * has no effect if the underlying DOM element is neither a text INPUT element
        2167 * nor a TEXTAREA element.
        2168 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        2169 * when the element has been cleared.
        2170 */
        2171webdriver.WebElement.prototype.clear = function() {
        2172 return this.schedule_(
        2173 new webdriver.Command(webdriver.CommandName.CLEAR_ELEMENT),
        2174 'WebElement.clear()');
        2175};
        2176
        2177
        2178/**
        2179 * Schedules a command to test whether this element is currently displayed.
        2180 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be
        2181 * resolved with whether this element is currently visible on the page.
        2182 */
        2183webdriver.WebElement.prototype.isDisplayed = function() {
        2184 return this.schedule_(
        2185 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_DISPLAYED),
        2186 'WebElement.isDisplayed()');
        2187};
        2188
        2189
        2190/**
        2191 * Schedules a command to retrieve the outer HTML of this element.
        2192 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        2193 * resolved with the element's outer HTML.
        2194 */
        2195webdriver.WebElement.prototype.getOuterHtml = function() {
        2196 return this.driver_.executeScript(function() {
        2197 var element = arguments[0];
        2198 if ('outerHTML' in element) {
        2199 return element.outerHTML;
        2200 } else {
        2201 var div = element.ownerDocument.createElement('div');
        2202 div.appendChild(element.cloneNode(true));
        2203 return div.innerHTML;
        2204 }
        2205 }, this);
        2206};
        2207
        2208
        2209/**
        2210 * Schedules a command to retrieve the inner HTML of this element.
        2211 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        2212 * resolved with the element's inner HTML.
        2213 */
        2214webdriver.WebElement.prototype.getInnerHtml = function() {
        2215 return this.driver_.executeScript('return arguments[0].innerHTML', this);
        2216};
        2217
        2218
        2219
        2220/**
        2221 * WebElementPromise is a promise that will be fulfilled with a WebElement.
        2222 * This serves as a forward proxy on WebElement, allowing calls to be
        2223 * scheduled without directly on this instance before the underlying
        2224 * WebElement has been fulfilled. In other words, the following two statements
        2225 * are equivalent:
        2226 *
        2227 * driver.findElement({id: 'my-button'}).click();
        2228 * driver.findElement({id: 'my-button'}).then(function(el) {
        2229 * return el.click();
        2230 * });
        2231 *
        2232 * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this
        2233 * element.
        2234 * @param {!webdriver.promise.Promise.<!webdriver.WebElement>} el A promise
        2235 * that will resolve to the promised element.
        2236 * @constructor
        2237 * @extends {webdriver.WebElement}
        2238 * @implements {webdriver.promise.Thenable.<!webdriver.WebElement>}
        2239 * @final
        2240 */
        2241webdriver.WebElementPromise = function(driver, el) {
        2242 webdriver.WebElement.call(this, driver, {'ELEMENT': 'unused'});
        2243
        2244 /** @override */
        2245 this.cancel = goog.bind(el.cancel, el);
        2246
        2247 /** @override */
        2248 this.isPending = goog.bind(el.isPending, el);
        2249
        2250 /** @override */
        2251 this.then = goog.bind(el.then, el);
        2252
        2253 /** @override */
        2254 this.thenCatch = goog.bind(el.thenCatch, el);
        2255
        2256 /** @override */
        2257 this.thenFinally = goog.bind(el.thenFinally, el);
        2258
        2259 /**
        2260 * Defers returning the element ID until the wrapped WebElement has been
        2261 * resolved.
        2262 * @override
        2263 */
        2264 this.getId = function() {
        2265 return el.then(function(el) {
        2266 return el.getId();
        2267 });
        2268 };
        2269};
        2270goog.inherits(webdriver.WebElementPromise, webdriver.WebElement);
        2271
        2272
        2273/**
        2274 * Represents a modal dialog such as {@code alert}, {@code confirm}, or
        2275 * {@code prompt}. Provides functions to retrieve the message displayed with
        2276 * the alert, accept or dismiss the alert, and set the response text (in the
        2277 * case of {@code prompt}).
        2278 * @param {!webdriver.WebDriver} driver The driver controlling the browser this
        2279 * alert is attached to.
        2280 * @param {string} text The message text displayed with this alert.
        2281 * @constructor
        2282 */
        2283webdriver.Alert = function(driver, text) {
        2284 /** @private {!webdriver.WebDriver} */
        2285 this.driver_ = driver;
        2286
        2287 /** @private {!webdriver.promise.Promise.<string>} */
        2288 this.text_ = webdriver.promise.when(text);
        2289};
        2290
        2291
        2292/**
        2293 * Retrieves the message text displayed with this alert. For instance, if the
        2294 * alert were opened with alert("hello"), then this would return "hello".
        2295 * @return {!webdriver.promise.Promise.<string>} A promise that will be
        2296 * resolved to the text displayed with this alert.
        2297 */
        2298webdriver.Alert.prototype.getText = function() {
        2299 return this.text_;
        2300};
        2301
        2302
        2303/**
        2304 * Accepts this alert.
        2305 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        2306 * when this command has completed.
        2307 */
        2308webdriver.Alert.prototype.accept = function() {
        2309 return this.driver_.schedule(
        2310 new webdriver.Command(webdriver.CommandName.ACCEPT_ALERT),
        2311 'WebDriver.switchTo().alert().accept()');
        2312};
        2313
        2314
        2315/**
        2316 * Dismisses this alert.
        2317 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        2318 * when this command has completed.
        2319 */
        2320webdriver.Alert.prototype.dismiss = function() {
        2321 return this.driver_.schedule(
        2322 new webdriver.Command(webdriver.CommandName.DISMISS_ALERT),
        2323 'WebDriver.switchTo().alert().dismiss()');
        2324};
        2325
        2326
        2327/**
        2328 * Sets the response text on this alert. This command will return an error if
        2329 * the underlying alert does not support response text (e.g. window.alert and
        2330 * window.confirm).
        2331 * @param {string} text The text to set.
        2332 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved
        2333 * when this command has completed.
        2334 */
        2335webdriver.Alert.prototype.sendKeys = function(text) {
        2336 return this.driver_.schedule(
        2337 new webdriver.Command(webdriver.CommandName.SET_ALERT_TEXT).
        2338 setParameter('text', text),
        2339 'WebDriver.switchTo().alert().sendKeys(' + text + ')');
        2340};
        2341
        2342
        2343
        2344/**
        2345 * AlertPromise is a promise that will be fulfilled with an Alert. This promise
        2346 * serves as a forward proxy on an Alert, allowing calls to be scheduled
        2347 * directly on this instance before the underlying Alert has been fulfilled. In
        2348 * other words, the following two statements are equivalent:
        2349 *
        2350 * driver.switchTo().alert().dismiss();
        2351 * driver.switchTo().alert().then(function(alert) {
        2352 * return alert.dismiss();
        2353 * });
        2354 *
        2355 * @param {!webdriver.WebDriver} driver The driver controlling the browser this
        2356 * alert is attached to.
        2357 * @param {!webdriver.promise.Thenable.<!webdriver.Alert>} alert A thenable
        2358 * that will be fulfilled with the promised alert.
        2359 * @constructor
        2360 * @extends {webdriver.Alert}
        2361 * @implements {webdriver.promise.Thenable.<!webdriver.Alert>}
        2362 * @final
        2363 */
        2364webdriver.AlertPromise = function(driver, alert) {
        2365 webdriver.Alert.call(this, driver, 'unused');
        2366
        2367 /** @override */
        2368 this.cancel = goog.bind(alert.cancel, alert);
        2369
        2370 /** @override */
        2371 this.isPending = goog.bind(alert.isPending, alert);
        2372
        2373 /** @override */
        2374 this.then = goog.bind(alert.then, alert);
        2375
        2376 /** @override */
        2377 this.thenCatch = goog.bind(alert.thenCatch, alert);
        2378
        2379 /** @override */
        2380 this.thenFinally = goog.bind(alert.thenFinally, alert);
        2381
        2382 /**
        2383 * Defer returning text until the promised alert has been resolved.
        2384 * @override
        2385 */
        2386 this.getText = function() {
        2387 return alert.then(function(alert) {
        2388 return alert.getText();
        2389 });
        2390 };
        2391
        2392 /**
        2393 * Defers action until the alert has been located.
        2394 * @override
        2395 */
        2396 this.accept = function() {
        2397 return alert.then(function(alert) {
        2398 return alert.accept();
        2399 });
        2400 };
        2401
        2402 /**
        2403 * Defers action until the alert has been located.
        2404 * @override
        2405 */
        2406 this.dismiss = function() {
        2407 return alert.then(function(alert) {
        2408 return alert.dismiss();
        2409 });
        2410 };
        2411
        2412 /**
        2413 * Defers action until the alert has been located.
        2414 * @override
        2415 */
        2416 this.sendKeys = function(text) {
        2417 return alert.then(function(alert) {
        2418 return alert.sendKeys(text);
        2419 });
        2420 };
        2421};
        2422goog.inherits(webdriver.AlertPromise, webdriver.Alert);
        2423
        2424
        2425
        2426/**
        2427 * An error returned to indicate that there is an unhandled modal dialog on the
        2428 * current page.
        2429 * @param {string} message The error message.
        2430 * @param {string} text The text displayed with the unhandled alert.
        2431 * @param {!webdriver.Alert} alert The alert handle.
        2432 * @constructor
        2433 * @extends {bot.Error}
        2434 */
        2435webdriver.UnhandledAlertError = function(message, text, alert) {
        2436 webdriver.UnhandledAlertError.base(
        2437 this, 'constructor', bot.ErrorCode.UNEXPECTED_ALERT_OPEN, message);
        2438
        2439 /** @private {string} */
        2440 this.text_ = text;
        2441
        2442 /** @private {!webdriver.Alert} */
        2443 this.alert_ = alert;
        2444};
        2445goog.inherits(webdriver.UnhandledAlertError, bot.Error);
        2446
        2447
        2448/**
        2449 * @return {string} The text displayed with the unhandled alert.
        2450 */
        2451webdriver.UnhandledAlertError.prototype.getAlertText = function() {
        2452 return this.text_;
        2453};
        2454
        2455
        2456
        2457/**
        2458 * Used with {@link webdriver.WebElement#sendKeys WebElement#sendKeys} on file
        2459 * input elements ({@code <input type="file">}) to detect when the entered key
        2460 * sequence defines the path to a file.
        2461 *
        2462 * By default, {@linkplain webdriver.WebElement WebElement's} will enter all
        2463 * key sequences exactly as entered. You may set a
        2464 * {@linkplain webdriver.WebDriver#setFileDetector file detector} on the parent
        2465 * WebDriver instance to define custom behavior for handling file elements. Of
        2466 * particular note is the {@link selenium-webdriver/remote.FileDetector}, which
        2467 * should be used when running against a remote
        2468 * [Selenium Server](http://docs.seleniumhq.org/download/).
        2469 */
        2470webdriver.FileDetector = goog.defineClass(null, {
        2471 /** @constructor */
        2472 constructor: function() {},
        2473
        2474 /**
        2475 * Handles the file specified by the given path, preparing it for use with
        2476 * the current browser. If the path does not refer to a valid file, it will
        2477 * be returned unchanged, otherwisee a path suitable for use with the current
        2478 * browser will be returned.
        2479 *
        2480 * This default implementation is a no-op. Subtypes may override this
        2481 * function for custom tailored file handling.
        2482 *
        2483 * @param {!webdriver.WebDriver} driver The driver for the current browser.
        2484 * @param {string} path The path to process.
        2485 * @return {!webdriver.promise.Promise<string>} A promise for the processed
        2486 * file path.
        2487 * @package
        2488 */
        2489 handleFile: function(driver, path) {
        2490 return webdriver.promise.fulfilled(path);
        2491 }
        2492});
        \ No newline at end of file diff --git a/docs/source/net/index.js.src.html b/docs/source/net/index.js.src.html index dbcd644..0cacfaa 100644 --- a/docs/source/net/index.js.src.html +++ b/docs/source/net/index.js.src.html @@ -1 +1 @@ -index.js

        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
        18var os = require('os');
        19
        20
        21function 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 */
        38function getAddress(loopback, opt_family) {
        39 var family = opt_family || 'IPv4';
        40 var addresses = [];
        41
        42 var interfaces;
        43 if (loopback) {
        44 interfaces = [getLoInterface()];
        45 }
        46 interfaces = interfaces || os.networkInterfaces();
        47 for (var key in interfaces) {
        48 interfaces[key].forEach(function(ipAddress) {
        49 if (ipAddress.family === family &&
        50 ipAddress.internal === loopback) {
        51 addresses.push(ipAddress.address);
        52 }
        53 });
        54 }
        55 return addresses[0];
        56}
        57
        58
        59// PUBLIC API
        60
        61
        62/**
        63 * Retrieves the external IP address for this host.
        64 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
        65 * @return {string} The IP address or undefined if not available.
        66 */
        67exports.getAddress = function(opt_family) {
        68 return getAddress(false, opt_family);
        69};
        70
        71
        72/**
        73 * Retrieves a loopback address for this machine.
        74 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
        75 * @return {string} The IP address or undefined if not available.
        76 */
        77exports.getLoopbackAddress = function(opt_family) {
        78 return getAddress(true, opt_family);
        79};
        \ No newline at end of file +index.js

        net/index.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18'use strict';
        19
        20var os = require('os');
        21
        22
        23function getLoInterface() {
        24 var name;
        25 if (process.platform === 'darwin') {
        26 name = 'lo0';
        27 } else if (process.platform === 'linux') {
        28 name = 'lo';
        29 }
        30 return name ? os.networkInterfaces()[name] : null;
        31}
        32
        33
        34/**
        35 * Queries the system network interfaces for an IP address.
        36 * @param {boolean} loopback Whether to find a loopback address.
        37 * @param {string=} opt_family The IP family (IPv4 or IPv6). Defaults to IPv4.
        38 * @return {string} The located IP address or undefined.
        39 */
        40function getAddress(loopback, opt_family) {
        41 var family = opt_family || 'IPv4';
        42 var addresses = [];
        43
        44 var interfaces;
        45 if (loopback) {
        46 var lo = getLoInterface();
        47 interfaces = lo ? [lo] : null;
        48 }
        49 interfaces = interfaces || os.networkInterfaces();
        50 for (var key in interfaces) {
        51 interfaces[key].forEach(function(ipAddress) {
        52 if (ipAddress.family === family &&
        53 ipAddress.internal === loopback) {
        54 addresses.push(ipAddress.address);
        55 }
        56 });
        57 }
        58 return addresses[0];
        59}
        60
        61
        62// PUBLIC API
        63
        64
        65/**
        66 * Retrieves the external IP address for this host.
        67 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
        68 * @return {string} The IP address or undefined if not available.
        69 */
        70exports.getAddress = function(opt_family) {
        71 return getAddress(false, opt_family);
        72};
        73
        74
        75/**
        76 * Retrieves a loopback address for this machine.
        77 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4".
        78 * @return {string} The IP address or undefined if not available.
        79 */
        80exports.getLoopbackAddress = function(opt_family) {
        81 return getAddress(true, opt_family);
        82};
        \ No newline at end of file diff --git a/docs/source/net/portprober.js.src.html b/docs/source/net/portprober.js.src.html index 115b0c9..591c440 100644 --- a/docs/source/net/portprober.js.src.html +++ b/docs/source/net/portprober.js.src.html @@ -1 +1 @@ -portprober.js

        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
        18var exec = require('child_process').exec,
        19 fs = require('fs'),
        20 net = require('net');
        21
        22var 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 */
        31var 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 */
        39var 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 */
        48function 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 */
        66function 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 */
        84function 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 */
        111function 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 */
        152function 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 */
        182function 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
        212exports.findFreePort = findFreePort;
        213exports.isFree = isFree;
        \ No newline at end of file +portprober.js

        net/portprober.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18'use strict';
        19
        20var exec = require('child_process').exec,
        21 fs = require('fs'),
        22 net = require('net');
        23
        24var promise = require('../index').promise;
        25
        26
        27/**
        28 * The IANA suggested ephemeral port range.
        29 * @type {{min: number, max: number}}
        30 * @const
        31 * @see http://en.wikipedia.org/wiki/Ephemeral_ports
        32 */
        33var DEFAULT_IANA_RANGE = {min: 49152, max: 65535};
        34
        35
        36/**
        37 * The epheremal port range for the current system. Lazily computed on first
        38 * access.
        39 * @type {webdriver.promise.Promise.<{min: number, max: number}>}
        40 */
        41var systemRange = null;
        42
        43
        44/**
        45 * Computes the ephemeral port range for the current system. This is based on
        46 * http://stackoverflow.com/a/924337.
        47 * @return {webdriver.promise.Promise.<{min: number, max: number}>} A promise
        48 * that will resolve to the ephemeral port range of the current system.
        49 */
        50function findSystemPortRange() {
        51 if (systemRange) {
        52 return systemRange;
        53 }
        54 var range = process.platform === 'win32' ?
        55 findWindowsPortRange() : findUnixPortRange();
        56 return systemRange = range.thenCatch(function() {
        57 return DEFAULT_IANA_RANGE;
        58 });
        59}
        60
        61
        62/**
        63 * Executes a command and returns its output if it succeeds.
        64 * @param {string} cmd The command to execute.
        65 * @return {!webdriver.promise.Promise.<string>} A promise that will resolve
        66 * with the command's stdout data.
        67 */
        68function execute(cmd) {
        69 var result = promise.defer();
        70 exec(cmd, function(err, stdout) {
        71 if (err) {
        72 result.reject(err);
        73 } else {
        74 result.fulfill(stdout);
        75 }
        76 });
        77 return result.promise;
        78}
        79
        80
        81/**
        82 * Computes the ephemeral port range for a Unix-like system.
        83 * @return {!webdriver.promise.Promise.<{min: number, max: number}>} A promise
        84 * that will resolve with the ephemeral port range on the current system.
        85 */
        86function findUnixPortRange() {
        87 var cmd;
        88 if (process.platform === 'sunos') {
        89 cmd =
        90 '/usr/sbin/ndd /dev/tcp tcp_smallest_anon_port tcp_largest_anon_port';
        91 } else if (fs.existsSync('/proc/sys/net/ipv4/ip_local_port_range')) {
        92 // Linux
        93 cmd = 'cat /proc/sys/net/ipv4/ip_local_port_range';
        94 } else {
        95 cmd = 'sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last' +
        96 ' | sed -e "s/.*:\\s*//"';
        97 }
        98
        99 return execute(cmd).then(function(stdout) {
        100 if (!stdout || !stdout.length) return DEFAULT_IANA_RANGE;
        101 var range = stdout.trim().split(/\s+/).map(Number);
        102 if (range.some(isNaN)) return DEFAULT_IANA_RANGE;
        103 return {min: range[0], max: range[1]};
        104 });
        105}
        106
        107
        108/**
        109 * Computes the ephemeral port range for a Windows system.
        110 * @return {!webdriver.promise.Promise.<{min: number, max: number}>} A promise
        111 * that will resolve with the ephemeral port range on the current system.
        112 */
        113function findWindowsPortRange() {
        114 var deferredRange = promise.defer();
        115 // First, check if we're running on XP. If this initial command fails,
        116 // we just fallback on the default IANA range.
        117 return execute('cmd.exe /c ver').then(function(stdout) {
        118 if (/Windows XP/.test(stdout)) {
        119 // TODO: Try to read these values from the registry.
        120 return {min: 1025, max: 5000};
        121 } else {
        122 return execute('netsh int ipv4 show dynamicport tcp').
        123 then(function(stdout) {
        124 /* > netsh int ipv4 show dynamicport tcp
        125 Protocol tcp Dynamic Port Range
        126 ---------------------------------
        127 Start Port : 49152
        128 Number of Ports : 16384
        129 */
        130 var range = stdout.split(/\n/).filter(function(line) {
        131 return /.*:\s*\d+/.test(line);
        132 }).map(function(line) {
        133 return Number(line.split(/:\s*/)[1]);
        134 });
        135
        136 return {
        137 min: range[0],
        138 max: range[0] + range[1]
        139 };
        140 });
        141 }
        142 });
        143}
        144
        145
        146/**
        147 * Tests if a port is free.
        148 * @param {number} port The port to test.
        149 * @param {string=} opt_host The bound host to test the {@code port} against.
        150 * Defaults to {@code INADDR_ANY}.
        151 * @return {!webdriver.promise.Promise.<boolean>} A promise that will resolve
        152 * with whether the port is free.
        153 */
        154function isFree(port, opt_host) {
        155 var result = promise.defer(function() {
        156 server.cancel();
        157 });
        158
        159 var server = net.createServer().on('error', function(e) {
        160 if (e.code === 'EADDRINUSE') {
        161 result.fulfill(false);
        162 } else {
        163 result.reject(e);
        164 }
        165 });
        166
        167 server.listen(port, opt_host, function() {
        168 server.close(function() {
        169 result.fulfill(true);
        170 });
        171 });
        172
        173 return result.promise;
        174}
        175
        176
        177/**
        178 * @param {string=} opt_host The bound host to test the {@code port} against.
        179 * Defaults to {@code INADDR_ANY}.
        180 * @return {!webdriver.promise.Promise.<number>} A promise that will resolve
        181 * to a free port. If a port cannot be found, the promise will be
        182 * rejected.
        183 */
        184function findFreePort(opt_host) {
        185 return findSystemPortRange().then(function(range) {
        186 var attempts = 0;
        187 var deferredPort = promise.defer();
        188 findPort();
        189 return deferredPort.promise;
        190
        191 function findPort() {
        192 attempts += 1;
        193 if (attempts > 10) {
        194 deferredPort.reject(Error('Unable to find a free port'));
        195 }
        196
        197 var port = Math.floor(
        198 Math.random() * (range.max - range.min) + range.min);
        199 isFree(port, opt_host).then(function(isFree) {
        200 if (isFree) {
        201 deferredPort.fulfill(port);
        202 } else {
        203 findPort();
        204 }
        205 });
        206 }
        207 });
        208}
        209
        210
        211// PUBLIC API
        212
        213
        214exports.findFreePort = findFreePort;
        215exports.isFree = isFree;
        \ No newline at end of file diff --git a/docs/source/opera.js.src.html b/docs/source/opera.js.src.html new file mode 100644 index 0000000..9d18944 --- /dev/null +++ b/docs/source/opera.js.src.html @@ -0,0 +1 @@ +opera.js

        opera.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines a {@linkplain Driver WebDriver} client for the
        20 * Opera web browser (v26+). Before using this module, you must download the
        21 * latest OperaDriver
        22 * [release](https://github.com/operasoftware/operachromiumdriver/releases) and
        23 * ensure it can be found on your system
        24 * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29).
        25 *
        26 * There are three primary classes exported by this module:
        27 *
        28 * 1. {@linkplain ServiceBuilder}: configures the
        29 * {@link selenium-webdriver/remote.DriverService remote.DriverService}
        30 * that manages the
        31 * [OperaDriver](https://github.com/operasoftware/operachromiumdriver)
        32 * child process.
        33 *
        34 * 2. {@linkplain Options}: defines configuration options for each new Opera
        35 * session, such as which {@linkplain Options#setProxy proxy} to use,
        36 * what {@linkplain Options#addExtensions extensions} to install, or
        37 * what {@linkplain Options#addArguments command-line switches} to use when
        38 * starting the browser.
        39 *
        40 * 3. {@linkplain Driver}: the WebDriver client; each new instance will control
        41 * a unique browser session with a clean user profile (unless otherwise
        42 * configured through the {@link Options} class).
        43 *
        44 * By default, every Opera session will use a single driver service, which is
        45 * started the first time a {@link Driver} instance is created and terminated
        46 * when this process exits. The default service will inherit its environment
        47 * from the current process and direct all output to /dev/null. You may obtain
        48 * a handle to this default service using
        49 * {@link #getDefaultService getDefaultService()} and change its configuration
        50 * with {@link #setDefaultService setDefaultService()}.
        51 *
        52 * You may also create a {@link Driver} with its own driver service. This is
        53 * useful if you need to capture the server's log output for a specific session:
        54 *
        55 * var opera = require('selenium-webdriver/opera');
        56 *
        57 * var service = new opera.ServiceBuilder()
        58 * .loggingTo('/my/log/file.txt')
        59 * .enableVerboseLogging()
        60 * .build();
        61 *
        62 * var options = new opera.Options();
        63 * // configure browser options ...
        64 *
        65 * var driver = new opera.Driver(options, service);
        66 *
        67 * Users should only instantiate the {@link Driver} class directly when they
        68 * need a custom driver service configuration (as shown above). For normal
        69 * operation, users should start Opera using the
        70 * {@link selenium-webdriver.Builder}.
        71 */
        72
        73'use strict';
        74
        75var fs = require('fs'),
        76 util = require('util');
        77
        78var webdriver = require('./index'),
        79 executors = require('./executors'),
        80 io = require('./io'),
        81 portprober = require('./net/portprober'),
        82 remote = require('./remote');
        83
        84
        85/**
        86 * Name of the OperaDriver executable.
        87 * @type {string}
        88 * @const
        89 */
        90var OPERADRIVER_EXE =
        91 process.platform === 'win32' ? 'operadriver.exe' : 'operadriver';
        92
        93
        94/**
        95 * Creates {@link remote.DriverService} instances that manages an
        96 * [OperaDriver](https://github.com/operasoftware/operachromiumdriver)
        97 * server in a child process.
        98 *
        99 * @param {string=} opt_exe Path to the server executable to use. If omitted,
        100 * the builder will attempt to locate the operadriver on the current
        101 * PATH.
        102 * @throws {Error} If provided executable does not exist, or the operadriver
        103 * cannot be found on the PATH.
        104 * @constructor
        105 */
        106var ServiceBuilder = function(opt_exe) {
        107 /** @private {string} */
        108 this.exe_ = opt_exe || io.findInPath(OPERADRIVER_EXE, true);
        109 if (!this.exe_) {
        110 throw Error(
        111 'The OperaDriver could not be found on the current PATH. Please ' +
        112 'download the latest version of the OperaDriver from ' +
        113 'https://github.com/operasoftware/operachromiumdriver/releases and ' +
        114 'ensure it can be found on your PATH.');
        115 }
        116
        117 if (!fs.existsSync(this.exe_)) {
        118 throw Error('File does not exist: ' + this.exe_);
        119 }
        120
        121 /** @private {!Array.<string>} */
        122 this.args_ = [];
        123 this.stdio_ = 'ignore';
        124};
        125
        126
        127/** @private {number} */
        128ServiceBuilder.prototype.port_ = 0;
        129
        130
        131/** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
        132ServiceBuilder.prototype.stdio_ = 'ignore';
        133
        134
        135/** @private {Object.<string, string>} */
        136ServiceBuilder.prototype.env_ = null;
        137
        138
        139/**
        140 * Sets the port to start the OperaDriver on.
        141 * @param {number} port The port to use, or 0 for any free port.
        142 * @return {!ServiceBuilder} A self reference.
        143 * @throws {Error} If the port is invalid.
        144 */
        145ServiceBuilder.prototype.usingPort = function(port) {
        146 if (port < 0) {
        147 throw Error('port must be >= 0: ' + port);
        148 }
        149 this.port_ = port;
        150 return this;
        151};
        152
        153
        154/**
        155 * Sets the path of the log file the driver should log to. If a log file is
        156 * not specified, the driver will log to stderr.
        157 * @param {string} path Path of the log file to use.
        158 * @return {!ServiceBuilder} A self reference.
        159 */
        160ServiceBuilder.prototype.loggingTo = function(path) {
        161 this.args_.push('--log-path=' + path);
        162 return this;
        163};
        164
        165
        166/**
        167 * Enables verbose logging.
        168 * @return {!ServiceBuilder} A self reference.
        169 */
        170ServiceBuilder.prototype.enableVerboseLogging = function() {
        171 this.args_.push('--verbose');
        172 return this;
        173};
        174
        175
        176/**
        177 * Silence sthe drivers output.
        178 * @return {!ServiceBuilder} A self reference.
        179 */
        180ServiceBuilder.prototype.silent = function() {
        181 this.args_.push('--silent');
        182 return this;
        183};
        184
        185
        186/**
        187 * Defines the stdio configuration for the driver service. See
        188 * {@code child_process.spawn} for more information.
        189 * @param {(string|!Array.<string|number|!Stream|null|undefined>)} config The
        190 * configuration to use.
        191 * @return {!ServiceBuilder} A self reference.
        192 */
        193ServiceBuilder.prototype.setStdio = function(config) {
        194 this.stdio_ = config;
        195 return this;
        196};
        197
        198
        199/**
        200 * Defines the environment to start the server under. This settings will be
        201 * inherited by every browser session started by the server.
        202 * @param {!Object.<string, string>} env The environment to use.
        203 * @return {!ServiceBuilder} A self reference.
        204 */
        205ServiceBuilder.prototype.withEnvironment = function(env) {
        206 this.env_ = env;
        207 return this;
        208};
        209
        210
        211/**
        212 * Creates a new DriverService using this instance's current configuration.
        213 * @return {remote.DriverService} A new driver service using this instance's
        214 * current configuration.
        215 * @throws {Error} If the driver exectuable was not specified and a default
        216 * could not be found on the current PATH.
        217 */
        218ServiceBuilder.prototype.build = function() {
        219 var port = this.port_ || portprober.findFreePort();
        220 var args = this.args_.concat(); // Defensive copy.
        221
        222 return new remote.DriverService(this.exe_, {
        223 loopback: true,
        224 port: port,
        225 args: webdriver.promise.when(port, function(port) {
        226 return args.concat('--port=' + port);
        227 }),
        228 env: this.env_,
        229 stdio: this.stdio_
        230 });
        231};
        232
        233
        234/** @type {remote.DriverService} */
        235var defaultService = null;
        236
        237
        238/**
        239 * Sets the default service to use for new OperaDriver instances.
        240 * @param {!remote.DriverService} service The service to use.
        241 * @throws {Error} If the default service is currently running.
        242 */
        243function setDefaultService(service) {
        244 if (defaultService && defaultService.isRunning()) {
        245 throw Error(
        246 'The previously configured OperaDriver service is still running. ' +
        247 'You must shut it down before you may adjust its configuration.');
        248 }
        249 defaultService = service;
        250}
        251
        252
        253/**
        254 * Returns the default OperaDriver service. If such a service has not been
        255 * configured, one will be constructed using the default configuration for
        256 * a OperaDriver executable found on the system PATH.
        257 * @return {!remote.DriverService} The default OperaDriver service.
        258 */
        259function getDefaultService() {
        260 if (!defaultService) {
        261 defaultService = new ServiceBuilder().build();
        262 }
        263 return defaultService;
        264}
        265
        266
        267/**
        268 * @type {string}
        269 * @const
        270 */
        271var OPTIONS_CAPABILITY_KEY = 'chromeOptions';
        272
        273
        274/**
        275 * Class for managing {@link Driver OperaDriver} specific options.
        276 * @constructor
        277 * @extends {webdriver.Serializable}
        278 */
        279var Options = function() {
        280 webdriver.Serializable.call(this);
        281
        282 /** @private {!Array.<string>} */
        283 this.args_ = [];
        284
        285 /** @private {!Array.<(string|!Buffer)>} */
        286 this.extensions_ = [];
        287};
        288util.inherits(Options, webdriver.Serializable);
        289
        290
        291/**
        292 * Extracts the OperaDriver specific options from the given capabilities
        293 * object.
        294 * @param {!webdriver.Capabilities} capabilities The capabilities object.
        295 * @return {!Options} The OperaDriver options.
        296 */
        297Options.fromCapabilities = function(capabilities) {
        298 var options;
        299 var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
        300 if (o instanceof Options) {
        301 options = o;
        302 } else if (o) {
        303 options.
        304 addArguments(o.args || []).
        305 addExtensions(o.extensions || []).
        306 setOperaBinaryPath(o.binary);
        307 } else {
        308 options = new Options;
        309 }
        310
        311 if (capabilities.has(webdriver.Capability.PROXY)) {
        312 options.setProxy(capabilities.get(webdriver.Capability.PROXY));
        313 }
        314
        315 if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) {
        316 options.setLoggingPrefs(
        317 capabilities.get(webdriver.Capability.LOGGING_PREFS));
        318 }
        319
        320 return options;
        321};
        322
        323
        324/**
        325 * Add additional command line arguments to use when launching the Opera
        326 * browser. Each argument may be specified with or without the "--" prefix
        327 * (e.g. "--foo" and "foo"). Arguments with an associated value should be
        328 * delimited by an "=": "foo=bar".
        329 * @param {...(string|!Array.<string>)} var_args The arguments to add.
        330 * @return {!Options} A self reference.
        331 */
        332Options.prototype.addArguments = function(var_args) {
        333 this.args_ = this.args_.concat.apply(this.args_, arguments);
        334 return this;
        335};
        336
        337
        338/**
        339 * Add additional extensions to install when launching Opera. Each extension
        340 * should be specified as the path to the packed CRX file, or a Buffer for an
        341 * extension.
        342 * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The
        343 * extensions to add.
        344 * @return {!Options} A self reference.
        345 */
        346Options.prototype.addExtensions = function(var_args) {
        347 this.extensions_ = this.extensions_.concat.apply(
        348 this.extensions_, arguments);
        349 return this;
        350};
        351
        352
        353/**
        354 * Sets the path to the Opera binary to use. On Mac OS X, this path should
        355 * reference the actual Opera executable, not just the application binary. The
        356 * binary path be absolute or relative to the operadriver server executable, but
        357 * it must exist on the machine that will launch Opera.
        358 *
        359 * @param {string} path The path to the Opera binary to use.
        360 * @return {!Options} A self reference.
        361 */
        362Options.prototype.setOperaBinaryPath = function(path) {
        363 this.binary_ = path;
        364 return this;
        365};
        366
        367
        368/**
        369 * Sets the logging preferences for the new session.
        370 * @param {!webdriver.logging.Preferences} prefs The logging preferences.
        371 * @return {!Options} A self reference.
        372 */
        373Options.prototype.setLoggingPrefs = function(prefs) {
        374 this.logPrefs_ = prefs;
        375 return this;
        376};
        377
        378
        379/**
        380 * Sets the proxy settings for the new session.
        381 * @param {webdriver.ProxyConfig} proxy The proxy configuration to use.
        382 * @return {!Options} A self reference.
        383 */
        384Options.prototype.setProxy = function(proxy) {
        385 this.proxy_ = proxy;
        386 return this;
        387};
        388
        389
        390/**
        391 * Converts this options instance to a {@link webdriver.Capabilities} object.
        392 * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge
        393 * these options into, if any.
        394 * @return {!webdriver.Capabilities} The capabilities.
        395 */
        396Options.prototype.toCapabilities = function(opt_capabilities) {
        397 var capabilities = opt_capabilities || webdriver.Capabilities.opera();
        398 capabilities.
        399 set(webdriver.Capability.PROXY, this.proxy_).
        400 set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_).
        401 set(OPTIONS_CAPABILITY_KEY, this);
        402 return capabilities;
        403};
        404
        405
        406/**
        407 * Converts this instance to its JSON wire protocol representation. Note this
        408 * function is an implementation not intended for general use.
        409 * @return {{args: !Array.<string>,
        410 * binary: (string|undefined),
        411 * detach: boolean,
        412 * extensions: !Array.<(string|!webdriver.promise.Promise.<string>))>,
        413 * localState: (Object|undefined),
        414 * logPath: (string|undefined),
        415 * prefs: (Object|undefined)}} The JSON wire protocol representation
        416 * of this instance.
        417 * @override
        418 */
        419Options.prototype.serialize = function() {
        420 var json = {
        421 args: this.args_,
        422 extensions: this.extensions_.map(function(extension) {
        423 if (Buffer.isBuffer(extension)) {
        424 return extension.toString('base64');
        425 }
        426 return webdriver.promise.checkedNodeCall(
        427 fs.readFile, extension, 'base64');
        428 })
        429 };
        430 if (this.binary_) {
        431 json.binary = this.binary_;
        432 }
        433 if (this.logFile_) {
        434 json.logPath = this.logFile_;
        435 }
        436 if (this.prefs_) {
        437 json.prefs = this.prefs_;
        438 }
        439
        440 return json;
        441};
        442
        443
        444/**
        445 * Creates a new WebDriver client for Opera.
        446 *
        447 * @param {(webdriver.Capabilities|Options)=} opt_config The configuration
        448 * options.
        449 * @param {remote.DriverService=} opt_service The session to use; will use
        450 * the {@link getDefaultService default service} by default.
        451 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
        452 * {@code null} to use the currently active flow.
        453 * @constructor
        454 * @extends {webdriver.WebDriver}
        455 */
        456var Driver = function(opt_config, opt_service, opt_flow) {
        457 var service = opt_service || getDefaultService();
        458 var executor = executors.createExecutor(service.start());
        459
        460 var capabilities =
        461 opt_config instanceof Options ? opt_config.toCapabilities() :
        462 (opt_config || webdriver.Capabilities.opera());
        463
        464 // On Linux, the OperaDriver does not look for Opera on the PATH, so we
        465 // must explicitly find it. See: operachromiumdriver #9.
        466 if (process.platform === 'linux') {
        467 var options = Options.fromCapabilities(capabilities);
        468 if (!options.binary_) {
        469 options.setOperaBinaryPath(io.findInPath('opera', true));
        470 }
        471 capabilities = options.toCapabilities(capabilities);
        472 }
        473
        474 var driver = webdriver.WebDriver.createSession(
        475 executor, capabilities, opt_flow);
        476
        477 webdriver.WebDriver.call(
        478 this, driver.getSession(), executor, driver.controlFlow());
        479};
        480util.inherits(Driver, webdriver.WebDriver);
        481
        482
        483/**
        484 * This function is a no-op as file detectors are not supported by this
        485 * implementation.
        486 * @override
        487 */
        488Driver.prototype.setFileDetector = function() {
        489};
        490
        491
        492// PUBLIC API
        493
        494
        495exports.Driver = Driver;
        496exports.Options = Options;
        497exports.ServiceBuilder = ServiceBuilder;
        498exports.getDefaultService = getDefaultService;
        499exports.setDefaultService = setDefaultService;
        \ No newline at end of file diff --git a/docs/source/phantomjs.js.src.html b/docs/source/phantomjs.js.src.html index 8988c7d..cc68bef 100644 --- a/docs/source/phantomjs.js.src.html +++ b/docs/source/phantomjs.js.src.html @@ -1 +1 @@ -phantomjs.js

        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
        18var fs = require('fs'),
        19 util = require('util');
        20
        21var 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 */
        34var 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 */
        43var 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 */
        51var 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 */
        59var 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 */
        69function 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 */
        91var 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 */
        106function 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
        164exports.createDriver = createDriver;
        \ No newline at end of file +phantomjs.js

        phantomjs.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18'use strict';
        19
        20var fs = require('fs'),
        21 util = require('util');
        22
        23var webdriver = require('./index'),
        24 executors = require('./executors'),
        25 http = require('./http'),
        26 io = require('./io'),
        27 portprober = require('./net/portprober'),
        28 remote = require('./remote');
        29
        30
        31/**
        32 * Name of the PhantomJS executable.
        33 * @type {string}
        34 * @const
        35 */
        36var PHANTOMJS_EXE =
        37 process.platform === 'win32' ? 'phantomjs.exe' : 'phantomjs';
        38
        39
        40/**
        41 * Capability that designates the location of the PhantomJS executable to use.
        42 * @type {string}
        43 * @const
        44 */
        45var BINARY_PATH_CAPABILITY = 'phantomjs.binary.path';
        46
        47
        48/**
        49 * Capability that designates the CLI arguments to pass to PhantomJS.
        50 * @type {string}
        51 * @const
        52 */
        53var CLI_ARGS_CAPABILITY = 'phantomjs.cli.args';
        54
        55
        56/**
        57 * Default log file to use if one is not specified through CLI args.
        58 * @type {string}
        59 * @const
        60 */
        61var DEFAULT_LOG_FILE = 'phantomjsdriver.log';
        62
        63
        64/**
        65 * Custom command names supported by PhantomJS.
        66 * @enum {string}
        67 */
        68var Command = {
        69 EXECUTE_PHANTOM_SCRIPT: 'executePhantomScript'
        70};
        71
        72
        73/**
        74 * Finds the PhantomJS executable.
        75 * @param {string=} opt_exe Path to the executable to use.
        76 * @return {string} The located executable.
        77 * @throws {Error} If the executable cannot be found on the PATH, or if the
        78 * provided executable path does not exist.
        79 */
        80function findExecutable(opt_exe) {
        81 var exe = opt_exe || io.findInPath(PHANTOMJS_EXE, true);
        82 if (!exe) {
        83 throw Error(
        84 'The PhantomJS executable could not be found on the current PATH. ' +
        85 'Please download the latest version from ' +
        86 'http://phantomjs.org/download.html and ensure it can be found on ' +
        87 'your PATH. For more information, see ' +
        88 'https://github.com/ariya/phantomjs/wiki');
        89 }
        90 if (!fs.existsSync(exe)) {
        91 throw Error('File does not exist: ' + exe);
        92 }
        93 return exe;
        94}
        95
        96
        97/**
        98 * Maps WebDriver logging level name to those recognised by PhantomJS.
        99 * @type {!Object.<string, string>}
        100 * @const
        101 */
        102var WEBDRIVER_TO_PHANTOMJS_LEVEL = (function() {
        103 var map = {};
        104 map[webdriver.logging.Level.ALL.name] = 'DEBUG';
        105 map[webdriver.logging.Level.DEBUG.name] = 'DEBUG';
        106 map[webdriver.logging.Level.INFO.name] = 'INFO';
        107 map[webdriver.logging.Level.WARNING.name] = 'WARN';
        108 map[webdriver.logging.Level.SEVERE.name] = 'ERROR';
        109 return map;
        110})();
        111
        112
        113/**
        114 * Creates a command executor with support for PhantomJS' custom commands.
        115 * @param {!webdriver.promise.Promise<string>} url The server's URL.
        116 * @return {!webdriver.CommandExecutor} The new command executor.
        117 */
        118function createExecutor(url) {
        119 return new executors.DeferredExecutor(url.then(function(url) {
        120 var client = new http.HttpClient(url);
        121 var executor = new http.Executor(client);
        122
        123 executor.defineCommand(
        124 Command.EXECUTE_PHANTOM_SCRIPT,
        125 'POST', '/session/:sessionId/phantom/execute');
        126
        127 return executor;
        128 }));
        129}
        130
        131/**
        132 * Creates a new WebDriver client for PhantomJS.
        133 *
        134 * @param {webdriver.Capabilities=} opt_capabilities The desired capabilities.
        135 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
        136 * {@code null} to use the currently active flow.
        137 * @constructor
        138 * @extends {webdriver.WebDriver}
        139 */
        140var Driver = function(opt_capabilities, opt_flow) {
        141 var capabilities = opt_capabilities || webdriver.Capabilities.phantomjs();
        142 var exe = findExecutable(capabilities.get(BINARY_PATH_CAPABILITY));
        143 var args = ['--webdriver-logfile=' + DEFAULT_LOG_FILE];
        144
        145 var logPrefs = capabilities.get(webdriver.Capability.LOGGING_PREFS);
        146 if (logPrefs instanceof webdriver.logging.Preferences) {
        147 logPrefs = logPrefs.toJSON();
        148 }
        149
        150 if (logPrefs && logPrefs[webdriver.logging.Type.DRIVER]) {
        151 var level = WEBDRIVER_TO_PHANTOMJS_LEVEL[
        152 logPrefs[webdriver.logging.Type.DRIVER]];
        153 if (level) {
        154 args.push('--webdriver-loglevel=' + level);
        155 }
        156 }
        157
        158 var proxy = capabilities.get(webdriver.Capability.PROXY);
        159 if (proxy) {
        160 switch (proxy.proxyType) {
        161 case 'manual':
        162 if (proxy.httpProxy) {
        163 args.push(
        164 '--proxy-type=http',
        165 '--proxy=http://' + proxy.httpProxy);
        166 }
        167 break;
        168 case 'pac':
        169 throw Error('PhantomJS does not support Proxy PAC files');
        170 case 'system':
        171 args.push('--proxy-type=system');
        172 break;
        173 case 'direct':
        174 args.push('--proxy-type=none');
        175 break;
        176 }
        177 }
        178 args = args.concat(capabilities.get(CLI_ARGS_CAPABILITY) || []);
        179
        180 var port = portprober.findFreePort();
        181 var service = new remote.DriverService(exe, {
        182 port: port,
        183 args: webdriver.promise.when(port, function(port) {
        184 args.push('--webdriver=' + port);
        185 return args;
        186 })
        187 });
        188
        189 var executor = createExecutor(service.start());
        190 var driver = webdriver.WebDriver.createSession(
        191 executor, capabilities, opt_flow);
        192
        193 webdriver.WebDriver.call(
        194 this, driver.getSession(), executor, driver.controlFlow());
        195
        196 var boundQuit = this.quit.bind(this);
        197
        198 /** @override */
        199 this.quit = function() {
        200 return boundQuit().thenFinally(service.kill.bind(service));
        201 };
        202};
        203util.inherits(Driver, webdriver.WebDriver);
        204
        205
        206/**
        207 * This function is a no-op as file detectors are not supported by this
        208 * implementation.
        209 * @override
        210 */
        211Driver.prototype.setFileDetector = function() {
        212};
        213
        214
        215/**
        216 * Executes a PhantomJS fragment. This method is similar to
        217 * {@link #executeScript}, except it exposes the
        218 * <a href="http://phantomjs.org/api/">PhantomJS API</a> to the injected
        219 * script.
        220 *
        221 * <p>The injected script will execute in the context of PhantomJS's
        222 * {@code page} variable. If a page has not been loaded before calling this
        223 * method, one will be created.</p>
        224 *
        225 * <p>Be sure to wrap callback definitions in a try/catch block, as failures
        226 * may cause future WebDriver calls to fail.</p>
        227 *
        228 * <p>Certain callbacks are used by GhostDriver (the PhantomJS WebDriver
        229 * implementation) and overriding these may cause the script to fail. It is
        230 * recommended that you check for existing callbacks before defining your own.
        231 * </p>
        232 *
        233 * As with {@link #executeScript}, the injected script may be defined as
        234 * a string for an anonymous function body (e.g. "return 123;"), or as a
        235 * function. If a function is provided, it will be decompiled to its original
        236 * source. Note that injecting functions is provided as a convenience to
        237 * simplify defining complex scripts. Care must be taken that the function
        238 * only references variables that will be defined in the page's scope and
        239 * that the function does not override {@code Function.prototype.toString}
        240 * (overriding toString() will interfere with how the function is
        241 * decompiled.
        242 *
        243 * @param {(string|!Function)} script The script to execute.
        244 * @param {...*} var_args The arguments to pass to the script.
        245 * @return {!webdriver.promise.Promise<T>} A promise that resolve to the
        246 * script's return value.
        247 * @template T
        248 */
        249Driver.prototype.executePhantomJS = function(script, args) {
        250 if (typeof script === 'function') {
        251 script = 'return (' + script + ').apply(this, arguments);';
        252 }
        253 var args = arguments.length > 1
        254 ? Array.prototype.slice.call(arguments, 1) : [];
        255 return this.schedule(
        256 new webdriver.Command(Command.EXECUTE_PHANTOM_SCRIPT)
        257 .setParameter('script', script)
        258 .setParameter('args', args),
        259 'Driver.executePhantomJS()');
        260};
        261
        262
        263// PUBLIC API
        264
        265exports.Driver = Driver;
        \ No newline at end of file diff --git a/docs/source/proxy.js.src.html b/docs/source/proxy.js.src.html index 70d3c1d..3424f55 100644 --- a/docs/source/proxy.js.src.html +++ b/docs/source/proxy.js.src.html @@ -1 +1 @@ -proxy.js

        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
        30var 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 */
        45var 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 */
        56exports.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 */
        83exports.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 */
        101exports.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 */
        113exports.system = function() {
        114 return {proxyType: 'system'};
        115};
        \ No newline at end of file +proxy.js

        proxy.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines functions for configuring a webdriver proxy:
        20 *
        21 * var webdriver = require('selenium-webdriver'),
        22 * proxy = require('selenium-webdriver/proxy');
        23 *
        24 * var driver = new webdriver.Builder()
        25 * .withCapabilities(webdriver.Capabilities.chrome())
        26 * .setProxy(proxy.manual({http: 'host:1234'}))
        27 * .build();
        28 */
        29
        30'use strict';
        31
        32var util = require('util');
        33
        34
        35
        36// PUBLIC API
        37
        38
        39/**
        40 * Configures WebDriver to bypass all browser proxies.
        41 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
        42 */
        43exports.direct = function() {
        44 return {proxyType: 'direct'};
        45};
        46
        47
        48/**
        49 * Manually configures the browser proxy. The following options are
        50 * supported:
        51 *
        52 * - `ftp`: Proxy host to use for FTP requests
        53 * - `http`: Proxy host to use for HTTP requests
        54 * - `https`: Proxy host to use for HTTPS requests
        55 * - `bypass`: A list of hosts requests should directly connect to,
        56 * bypassing any other proxies for that request. May be specified as a
        57 * comma separated string, or a list of strings.
        58 *
        59 * Behavior is undefined for FTP, HTTP, and HTTPS requests if the
        60 * corresponding key is omitted from the configuration options.
        61 *
        62 * @param {{ftp: (string|undefined),
        63 * http: (string|undefined),
        64 * https: (string|undefined),
        65 * bypass: (string|!Array.<string>|undefined)}} options Proxy
        66 * configuration options.
        67 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
        68 */
        69exports.manual = function(options) {
        70 return {
        71 proxyType: 'manual',
        72 ftpProxy: options.ftp,
        73 httpProxy: options.http,
        74 sslProxy: options.https,
        75 noProxy: util.isArray(options.bypass) ?
        76 options.bypass.join(',') : options.bypass
        77 };
        78};
        79
        80
        81/**
        82 * Configures WebDriver to configure the browser proxy using the PAC file at
        83 * the given URL.
        84 * @param {string} url URL for the PAC proxy to use.
        85 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
        86 */
        87exports.pac = function(url) {
        88 return {
        89 proxyType: 'pac',
        90 proxyAutoconfigUrl: url
        91 };
        92};
        93
        94
        95/**
        96 * Configures WebDriver to use the current system's proxy.
        97 * @return {!webdriver.ProxyConfig} A new proxy configuration object.
        98 */
        99exports.system = function() {
        100 return {proxyType: 'system'};
        101};
        \ No newline at end of file diff --git a/docs/source/remote/index.js.src.html b/docs/source/remote/index.js.src.html index a6a3b48..4951c23 100644 --- a/docs/source/remote/index.js.src.html +++ b/docs/source/remote/index.js.src.html @@ -1 +1 @@ -index.js

        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
        17var spawn = require('child_process').spawn,
        18 os = require('os'),
        19 path = require('path'),
        20 url = require('url'),
        21 util = require('util');
        22
        23var 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>{@code port} - The port to start the server on (must be > 0). If the
        34 * port is provided as a promise, the service will wait for the promise to
        35 * resolve before starting.
        36 * <li>{@code args} - The arguments to pass to the service. If a promise is
        37 * provided, the service will wait for it to resolve before starting.
        38 * <li>{@code path} - The base path on the server for the WebDriver wire
        39 * protocol (e.g. '/wd/hub'). Defaults to '/'.
        40 * <li>{@code env} - The environment variables that should be visible to the
        41 * server process. Defaults to inheriting the current process's
        42 * environment.
        43 * <li>{@code stdio} - IO configuration for the spawned server process. For
        44 * more information, refer to the documentation of
        45 * {@code child_process.spawn}.
        46 * </ul>
        47 *
        48 * @typedef {{
        49 * port: (number|!webdriver.promise.Promise.<number>),
        50 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
        51 * path: (string|undefined),
        52 * env: (!Object.<string, string>|undefined),
        53 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
        54 * }}
        55 */
        56var ServiceOptions;
        57
        58
        59/**
        60 * Manages the life and death of a native executable WebDriver server.
        61 *
        62 * <p>It is expected that the driver server implements the
        63 * <a href="http://code.google.com/p/selenium/wiki/JsonWireProtocol">WebDriver
        64 * Wire Protocol</a>. Furthermore, the managed server should support multiple
        65 * concurrent sessions, so that this class may be reused for multiple clients.
        66 *
        67 * @param {string} executable Path to the executable to run.
        68 * @param {!ServiceOptions} options Configuration options for the service.
        69 * @constructor
        70 */
        71function DriverService(executable, options) {
        72
        73 /** @private {string} */
        74 this.executable_ = executable;
        75
        76 /** @private {(number|!webdriver.promise.Promise.<number>)} */
        77 this.port_ = options.port;
        78
        79 /**
        80 * @private {!(Array.<string>|webdriver.promise.Promise.<!Array.<string>>)}
        81 */
        82 this.args_ = options.args;
        83
        84 /** @private {string} */
        85 this.path_ = options.path || '/';
        86
        87 /** @private {!Object.<string, string>} */
        88 this.env_ = options.env || process.env;
        89
        90 /** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
        91 this.stdio_ = options.stdio || 'ignore';
        92}
        93
        94
        95/**
        96 * The default amount of time, in milliseconds, to wait for the server to
        97 * start.
        98 * @type {number}
        99 */
        100DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000;
        101
        102
        103/** @private {child_process.ChildProcess} */
        104DriverService.prototype.process_ = null;
        105
        106
        107/**
        108 * Promise that resolves to the server's address or null if the server has not
        109 * been started.
        110 * @private {webdriver.promise.Promise.<string>}
        111 */
        112DriverService.prototype.address_ = null;
        113
        114
        115/**
        116 * Promise that tracks the status of shutting down the server, or null if the
        117 * server is not currently shutting down.
        118 * @private {webdriver.promise.Promise}
        119 */
        120DriverService.prototype.shutdownHook_ = null;
        121
        122
        123/**
        124 * @return {!webdriver.promise.Promise.<string>} A promise that resolves to
        125 * the server's address.
        126 * @throws {Error} If the server has not been started.
        127 */
        128DriverService.prototype.address = function() {
        129 if (this.address_) {
        130 return this.address_;
        131 }
        132 throw Error('Server has not been started.');
        133};
        134
        135
        136/**
        137 * @return {boolean} Whether the underlying service process is running.
        138 */
        139DriverService.prototype.isRunning = function() {
        140 return !!this.address_;
        141};
        142
        143
        144/**
        145 * Starts the server if it is not already running.
        146 * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the
        147 * server to start accepting requests. Defaults to 30 seconds.
        148 * @return {!webdriver.promise.Promise.<string>} A promise that will resolve
        149 * to the server's base URL when it has started accepting requests. If the
        150 * timeout expires before the server has started, the promise will be
        151 * rejected.
        152 */
        153DriverService.prototype.start = function(opt_timeoutMs) {
        154 if (this.address_) {
        155 return this.address_;
        156 }
        157
        158 var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS;
        159
        160 var self = this;
        161 this.address_ = promise.defer();
        162 this.address_.fulfill(promise.when(this.port_, function(port) {
        163 if (port <= 0) {
        164 throw Error('Port must be > 0: ' + port);
        165 }
        166 return promise.when(self.args_, function(args) {
        167 self.process_ = spawn(self.executable_, args, {
        168 env: self.env_,
        169 stdio: self.stdio_
        170 }).once('exit', onServerExit);
        171
        172 // This process should not wait on the spawned child, however, we do
        173 // want to ensure the child is killed when this process exits.
        174 self.process_.unref();
        175 process.once('exit', killServer);
        176
        177 var serverUrl = url.format({
        178 protocol: 'http',
        179 hostname: net.getAddress() || net.getLoopbackAddress(),
        180 port: port,
        181 pathname: self.path_
        182 });
        183
        184 return httpUtil.waitForServer(serverUrl, timeout).then(function() {
        185 return serverUrl;
        186 });
        187 });
        188 }));
        189
        190 return this.address_;
        191
        192 function onServerExit(code, signal) {
        193 self.address_.reject(code == null ?
        194 Error('Server was killed with ' + signal) :
        195 Error('Server exited with ' + code));
        196
        197 if (self.shutdownHook_) {
        198 self.shutdownHook_.fulfill();
        199 }
        200
        201 self.shutdownHook_ = null;
        202 self.address_ = null;
        203 self.process_ = null;
        204 process.removeListener('exit', killServer);
        205 }
        206
        207 function killServer() {
        208 process.removeListener('exit', killServer);
        209 self.process_ && self.process_.kill('SIGTERM');
        210 }
        211};
        212
        213
        214/**
        215 * Stops the service if it is not currently running. This function will kill
        216 * the server immediately. To synchronize with the active control flow, use
        217 * {@link #stop()}.
        218 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        219 * the server has been stopped.
        220 */
        221DriverService.prototype.kill = function() {
        222 if (!this.address_) {
        223 return promise.fulfilled(); // Not currently running.
        224 }
        225
        226 if (!this.shutdownHook_) {
        227 // No process: still starting; wait on address.
        228 // Otherwise, kill the process now. Exit handler will resolve the
        229 // shutdown hook.
        230 if (this.process_) {
        231 this.shutdownHook_ = promise.defer();
        232 this.process_.kill('SIGTERM');
        233 } else {
        234 var self = this;
        235 this.shutdownHook_ = this.address_.thenFinally(function() {
        236 self.process_ && self.process_.kill('SIGTERM');
        237 });
        238 }
        239 }
        240
        241 return this.shutdownHook_;
        242};
        243
        244
        245/**
        246 * Schedules a task in the current control flow to stop the server if it is
        247 * currently running.
        248 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        249 * the server has been stopped.
        250 */
        251DriverService.prototype.stop = function() {
        252 return promise.controlFlow().execute(this.kill.bind(this));
        253};
        254
        255
        256
        257/**
        258 * Manages the life and death of the Selenium standalone server. The server
        259 * may be obtained from http://selenium-release.storage.googleapis.com/index.html.
        260 * @param {string} jar Path to the Selenium server jar.
        261 * @param {!SeleniumServer.Options} options Configuration options for the
        262 * server.
        263 * @throws {Error} If an invalid port is specified.
        264 * @constructor
        265 * @extends {DriverService}
        266 */
        267function SeleniumServer(jar, options) {
        268 if (options.port < 0)
        269 throw Error('Port must be >= 0: ' + options.port);
        270
        271 var port = options.port || portprober.findFreePort();
        272 var args = promise.when(options.jvmArgs || [], function(jvmArgs) {
        273 return promise.when(options.args || [], function(args) {
        274 return promise.when(port, function(port) {
        275 return jvmArgs.concat(['-jar', jar, '-port', port]).concat(args);
        276 });
        277 });
        278 });
        279
        280 DriverService.call(this, 'java', {
        281 port: port,
        282 args: args,
        283 path: '/wd/hub',
        284 env: options.env,
        285 stdio: options.stdio
        286 });
        287}
        288util.inherits(SeleniumServer, DriverService);
        289
        290
        291/**
        292 * Options for the Selenium server:
        293 * <ul>
        294 * <li>{@code port} - The port to start the server on (must be > 0). If the
        295 * port is provided as a promise, the service will wait for the promise to
        296 * resolve before starting.
        297 * <li>{@code args} - The arguments to pass to the service. If a promise is
        298 * provided, the service will wait for it to resolve before starting.
        299 * <li>{@code jvmArgs} - The arguments to pass to the JVM. If a promise is
        300 * provided, the service will wait for it to resolve before starting.
        301 * <li>{@code env} - The environment variables that should be visible to the
        302 * server process. Defaults to inheriting the current process's
        303 * environment.
        304 * <li>{@code stdio} - IO configuration for the spawned server process. For
        305 * more information, refer to the documentation of
        306 * {@code child_process.spawn}.
        307 * </ul>
        308 *
        309 * @typedef {{
        310 * port: (number|!webdriver.promise.Promise.<number>),
        311 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
        312 * jvmArgs: (!Array.<string>|
        313 * !webdriver.promise.Promise.<!Array.<string>>|
        314 * undefined),
        315 * env: (!Object.<string, string>|undefined),
        316 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
        317 * }}
        318 */
        319SeleniumServer.Options;
        320
        321
        322// PUBLIC API
        323
        324exports.DriverService = DriverService;
        325exports.SeleniumServer = SeleniumServer;
        \ No newline at end of file +index.js

        remote/index.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18'use strict';
        19
        20var AdmZip = require('adm-zip'),
        21 AdmConstants = require('adm-zip/util/constants'),
        22 fs = require('fs'),
        23 path = require('path'),
        24 url = require('url'),
        25 util = require('util');
        26
        27var _base = require('../_base'),
        28 webdriver = require('../'),
        29 promise = require('../').promise,
        30 httpUtil = require('../http/util'),
        31 exec = require('../io/exec'),
        32 net = require('../net'),
        33 portprober = require('../net/portprober');
        34
        35
        36
        37/**
        38 * Configuration options for a DriverService instance.
        39 *
        40 * - `loopback` - Whether the service should only be accessed on this host's
        41 * loopback address.
        42 * - `port` - The port to start the server on (must be > 0). If the port is
        43 * provided as a promise, the service will wait for the promise to resolve
        44 * before starting.
        45 * - `args` - The arguments to pass to the service. If a promise is provided,
        46 * the service will wait for it to resolve before starting.
        47 * - `path` - The base path on the server for the WebDriver wire protocol
        48 * (e.g. '/wd/hub'). Defaults to '/'.
        49 * - `env` - The environment variables that should be visible to the server
        50 * process. Defaults to inheriting the current process's environment.
        51 * - `stdio` - IO configuration for the spawned server process. For more
        52 * information, refer to the documentation of `child_process.spawn`.
        53 *
        54 * @typedef {{
        55 * loopback: (boolean|undefined),
        56 * port: (number|!webdriver.promise.Promise.<number>),
        57 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
        58 * path: (string|undefined),
        59 * env: (!Object.<string, string>|undefined),
        60 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
        61 * }}
        62 */
        63var ServiceOptions;
        64
        65
        66/**
        67 * Manages the life and death of a native executable WebDriver server.
        68 *
        69 * It is expected that the driver server implements the
        70 * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol.
        71 * Furthermore, the managed server should support multiple concurrent sessions,
        72 * so that this class may be reused for multiple clients.
        73 *
        74 * @param {string} executable Path to the executable to run.
        75 * @param {!ServiceOptions} options Configuration options for the service.
        76 * @constructor
        77 */
        78function DriverService(executable, options) {
        79
        80 /** @private {string} */
        81 this.executable_ = executable;
        82
        83 /** @private {boolean} */
        84 this.loopbackOnly_ = !!options.loopback;
        85
        86 /** @private {(number|!webdriver.promise.Promise.<number>)} */
        87 this.port_ = options.port;
        88
        89 /**
        90 * @private {!(Array.<string>|webdriver.promise.Promise.<!Array.<string>>)}
        91 */
        92 this.args_ = options.args;
        93
        94 /** @private {string} */
        95 this.path_ = options.path || '/';
        96
        97 /** @private {!Object.<string, string>} */
        98 this.env_ = options.env || process.env;
        99
        100 /** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */
        101 this.stdio_ = options.stdio || 'ignore';
        102
        103 /**
        104 * A promise for the managed subprocess, or null if the server has not been
        105 * started yet. This promise will never be rejected.
        106 * @private {promise.Promise.<!exec.Command>}
        107 */
        108 this.command_ = null;
        109
        110 /**
        111 * Promise that resolves to the server's address or null if the server has
        112 * not been started. This promise will be rejected if the server terminates
        113 * before it starts accepting WebDriver requests.
        114 * @private {promise.Promise.<string>}
        115 */
        116 this.address_ = null;
        117}
        118
        119
        120/**
        121 * The default amount of time, in milliseconds, to wait for the server to
        122 * start.
        123 * @type {number}
        124 */
        125DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000;
        126
        127
        128/**
        129 * @return {!webdriver.promise.Promise.<string>} A promise that resolves to
        130 * the server's address.
        131 * @throws {Error} If the server has not been started.
        132 */
        133DriverService.prototype.address = function() {
        134 if (this.address_) {
        135 return this.address_;
        136 }
        137 throw Error('Server has not been started.');
        138};
        139
        140
        141/**
        142 * Returns whether the underlying process is still running. This does not take
        143 * into account whether the process is in the process of shutting down.
        144 * @return {boolean} Whether the underlying service process is running.
        145 */
        146DriverService.prototype.isRunning = function() {
        147 return !!this.address_;
        148};
        149
        150
        151/**
        152 * Starts the server if it is not already running.
        153 * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the
        154 * server to start accepting requests. Defaults to 30 seconds.
        155 * @return {!promise.Promise.<string>} A promise that will resolve
        156 * to the server's base URL when it has started accepting requests. If the
        157 * timeout expires before the server has started, the promise will be
        158 * rejected.
        159 */
        160DriverService.prototype.start = function(opt_timeoutMs) {
        161 if (this.address_) {
        162 return this.address_;
        163 }
        164
        165 var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS;
        166
        167 var self = this;
        168 this.command_ = promise.defer();
        169 this.address_ = promise.defer();
        170 this.address_.fulfill(promise.when(this.port_, function(port) {
        171 if (port <= 0) {
        172 throw Error('Port must be > 0: ' + port);
        173 }
        174 return promise.when(self.args_, function(args) {
        175 var command = exec(self.executable_, {
        176 args: args,
        177 env: self.env_,
        178 stdio: self.stdio_
        179 });
        180
        181 self.command_.fulfill(command);
        182
        183 var earlyTermination = command.result().then(function(result) {
        184 var error = result.code == null ?
        185 Error('Server was killed with ' + result.signal) :
        186 Error('Server terminated early with status ' + result.code);
        187 self.address_.reject(error);
        188 self.address_ = null;
        189 self.command_ = null;
        190 throw error;
        191 });
        192
        193 var serverUrl = url.format({
        194 protocol: 'http',
        195 hostname: !self.loopbackOnly_ && net.getAddress() ||
        196 net.getLoopbackAddress(),
        197 port: port,
        198 pathname: self.path_
        199 });
        200
        201 return new promise.Promise(function(fulfill, reject) {
        202 var ready = httpUtil.waitForServer(serverUrl, timeout)
        203 .then(fulfill, reject);
        204 earlyTermination.thenCatch(function(e) {
        205 ready.cancel(e);
        206 reject(Error(e.message));
        207 });
        208 }).then(function() {
        209 return serverUrl;
        210 });
        211 });
        212 }));
        213
        214 return this.address_;
        215};
        216
        217
        218/**
        219 * Stops the service if it is not currently running. This function will kill
        220 * the server immediately. To synchronize with the active control flow, use
        221 * {@link #stop()}.
        222 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        223 * the server has been stopped.
        224 */
        225DriverService.prototype.kill = function() {
        226 if (!this.address_ || !this.command_) {
        227 return promise.fulfilled(); // Not currently running.
        228 }
        229 return this.command_.then(function(command) {
        230 command.kill('SIGTERM');
        231 });
        232};
        233
        234
        235/**
        236 * Schedules a task in the current control flow to stop the server if it is
        237 * currently running.
        238 * @return {!webdriver.promise.Promise} A promise that will be resolved when
        239 * the server has been stopped.
        240 */
        241DriverService.prototype.stop = function() {
        242 return promise.controlFlow().execute(this.kill.bind(this));
        243};
        244
        245
        246
        247/**
        248 * Manages the life and death of the
        249 * <a href="http://selenium-release.storage.googleapis.com/index.html">
        250 * standalone Selenium server</a>.
        251 *
        252 * @param {string} jar Path to the Selenium server jar.
        253 * @param {SeleniumServer.Options=} opt_options Configuration options for the
        254 * server.
        255 * @throws {Error} If the path to the Selenium jar is not specified or if an
        256 * invalid port is specified.
        257 * @constructor
        258 * @extends {DriverService}
        259 */
        260function SeleniumServer(jar, opt_options) {
        261 if (!jar) {
        262 throw Error('Path to the Selenium jar not specified');
        263 }
        264
        265 var options = opt_options || {};
        266
        267 if (options.port < 0) {
        268 throw Error('Port must be >= 0: ' + options.port);
        269 }
        270
        271 var port = options.port || portprober.findFreePort();
        272 var args = promise.when(options.jvmArgs || [], function(jvmArgs) {
        273 return promise.when(options.args || [], function(args) {
        274 return promise.when(port, function(port) {
        275 return jvmArgs.concat(['-jar', jar, '-port', port]).concat(args);
        276 });
        277 });
        278 });
        279
        280 DriverService.call(this, 'java', {
        281 loopback: options.loopback,
        282 port: port,
        283 args: args,
        284 path: '/wd/hub',
        285 env: options.env,
        286 stdio: options.stdio
        287 });
        288}
        289util.inherits(SeleniumServer, DriverService);
        290
        291
        292/**
        293 * Options for the Selenium server:
        294 *
        295 * - `loopback` - Whether the server should only be accessed on this host's
        296 * loopback address.
        297 * - `port` - The port to start the server on (must be > 0). If the port is
        298 * provided as a promise, the service will wait for the promise to resolve
        299 * before starting.
        300 * - `args` - The arguments to pass to the service. If a promise is provided,
        301 * the service will wait for it to resolve before starting.
        302 * - `jvmArgs` - The arguments to pass to the JVM. If a promise is provided,
        303 * the service will wait for it to resolve before starting.
        304 * - `env` - The environment variables that should be visible to the server
        305 * process. Defaults to inheriting the current process's environment.
        306 * - `stdio` - IO configuration for the spawned server process. For more
        307 * information, refer to the documentation of `child_process.spawn`.
        308 *
        309 * @typedef {{
        310 * loopback: (boolean|undefined),
        311 * port: (number|!webdriver.promise.Promise.<number>),
        312 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>),
        313 * jvmArgs: (!Array.<string>|
        314 * !webdriver.promise.Promise.<!Array.<string>>|
        315 * undefined),
        316 * env: (!Object.<string, string>|undefined),
        317 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined)
        318 * }}
        319 */
        320SeleniumServer.Options;
        321
        322
        323
        324/**
        325 * A {@link webdriver.FileDetector} that may be used when running
        326 * against a remote
        327 * [Selenium server](http://selenium-release.storage.googleapis.com/index.html).
        328 *
        329 * When a file path on the local machine running this script is entered with
        330 * {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector
        331 * will transfer the specified file to the Selenium server's host; the sendKeys
        332 * command will be updated to use the transfered file's path.
        333 *
        334 * __Note:__ This class depends on a non-standard command supported on the
        335 * Java Selenium server. The file detector will fail if used with a server that
        336 * only supports standard WebDriver commands (such as the ChromeDriver).
        337 *
        338 * @constructor
        339 * @extends {webdriver.FileDetector}
        340 * @final
        341 */
        342var FileDetector = function() {};
        343util.inherits(webdriver.FileDetector, FileDetector);
        344
        345
        346/** @override */
        347FileDetector.prototype.handleFile = function(driver, filePath) {
        348 return promise.checkedNodeCall(fs.stat, filePath).then(function(stats) {
        349 if (stats.isDirectory()) {
        350 throw TypeError('Uploading directories is not supported: ' + filePath);
        351 }
        352
        353 var zip = new AdmZip();
        354 zip.addLocalFile(filePath);
        355 zip.getEntries()[0].header.method = AdmConstants.STORED;
        356
        357 var command = new webdriver.Command(webdriver.CommandName.UPLOAD_FILE)
        358 .setParameter('file', zip.toBuffer().toString('base64'));
        359 return driver.schedule(command,
        360 'remote.FileDetector.handleFile(' + filePath + ')');
        361 }, function(err) {
        362 if (err.code === 'ENOENT') {
        363 return filePath; // Not a file; return original input.
        364 }
        365 throw err;
        366 });
        367};
        368
        369// PUBLIC API
        370
        371exports.DriverService = DriverService;
        372exports.FileDetector = FileDetector;
        373exports.SeleniumServer = SeleniumServer;
        \ No newline at end of file diff --git a/docs/source/safari.js.src.html b/docs/source/safari.js.src.html new file mode 100644 index 0000000..60698f3 --- /dev/null +++ b/docs/source/safari.js.src.html @@ -0,0 +1 @@ +safari.js

        safari.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines a WebDriver client for Safari. Before using this
        20 * module, you must install the
        21 * [latest version](http://selenium-release.storage.googleapis.com/index.html)
        22 * of the SafariDriver browser extension; using Safari for normal browsing is
        23 * not recommended once the extension has been installed. You can, and should,
        24 * disable the extension when the browser is not being used with WebDriver.
        25 */
        26
        27'use strict';
        28
        29var events = require('events');
        30var fs = require('fs');
        31var http = require('http');
        32var path = require('path');
        33var url = require('url');
        34var util = require('util');
        35var ws = require('ws');
        36
        37var webdriver = require('./');
        38var promise = webdriver.promise;
        39var _base = require('./_base');
        40var io = require('./io');
        41var exec = require('./io/exec');
        42var portprober = require('./net/portprober');
        43
        44
        45/** @const */
        46var CLIENT_PATH = _base.isDevMode()
        47 ? path.join(__dirname,
        48 '../../../build/javascript/safari-driver/client.js')
        49 : path.join(__dirname, 'lib/safari/client.js');
        50
        51
        52/** @const */
        53var LIBRARY_DIR = process.platform === 'darwin'
        54 ? path.join('/Users', process.env['USER'], 'Library/Safari')
        55 : path.join(process.env['APPDATA'], 'Apple Computer', 'Safari');
        56
        57
        58/** @const */
        59var SESSION_DATA_FILES = (function() {
        60 if (process.platform === 'darwin') {
        61 var libraryDir = path.join('/Users', process.env['USER'], 'Library');
        62 return [
        63 path.join(libraryDir, 'Caches/com.apple.Safari/Cache.db'),
        64 path.join(libraryDir, 'Cookies/Cookies.binarycookies'),
        65 path.join(libraryDir, 'Cookies/Cookies.plist'),
        66 path.join(libraryDir, 'Safari/History.plist'),
        67 path.join(libraryDir, 'Safari/LastSession.plist'),
        68 path.join(libraryDir, 'Safari/LocalStorage'),
        69 path.join(libraryDir, 'Safari/Databases')
        70 ];
        71 } else if (process.platform === 'win32') {
        72 var appDataDir = path.join(process.env['APPDATA'],
        73 'Apple Computer', 'Safari');
        74 var localDataDir = path.join(process.env['LOCALAPPDATA'],
        75 'Apple Computer', 'Safari');
        76 return [
        77 path.join(appDataDir, 'History.plist'),
        78 path.join(appDataDir, 'LastSession.plist'),
        79 path.join(appDataDir, 'Cookies/Cookies.plist'),
        80 path.join(appDataDir, 'Cookies/Cookies.binarycookies'),
        81 path.join(localDataDir, 'Cache.db'),
        82 path.join(localDataDir, 'Databases'),
        83 path.join(localDataDir, 'LocalStorage')
        84 ];
        85 } else {
        86 return [];
        87 }
        88})();
        89
        90
        91/** @typedef {{port: number, address: string, family: string}} */
        92var Host;
        93
        94
        95/**
        96 * A basic HTTP/WebSocket server used to communicate with the SafariDriver
        97 * browser extension.
        98 * @constructor
        99 * @extends {events.EventEmitter}
        100 */
        101var Server = function() {
        102 events.EventEmitter.call(this);
        103
        104 var server = http.createServer(function(req, res) {
        105 if (req.url === '/favicon.ico') {
        106 res.writeHead(204);
        107 res.end();
        108 return;
        109 }
        110
        111 var query = url.parse(req.url).query || '';
        112 if (query.indexOf('url=') == -1) {
        113 var address = server.address()
        114 var host = address.address + ':' + address.port;
        115 res.writeHead(302, {'Location': 'http://' + host + '?url=ws://' + host});
        116 res.end();
        117 }
        118
        119 fs.readFile(CLIENT_PATH, 'utf8', function(err, data) {
        120 if (err) {
        121 res.writeHead(500, {'Content-Type': 'text/plain'});
        122 res.end(err.stack);
        123 return;
        124 }
        125 var content = '<!DOCTYPE html><body><script>' + data + '</script>';
        126 res.writeHead(200, {
        127 'Content-Type': 'text/html; charset=utf-8',
        128 'Content-Length': Buffer.byteLength(content, 'utf8'),
        129 });
        130 res.end(content);
        131 });
        132 });
        133
        134 var wss = new ws.Server({server: server});
        135 wss.on('connection', this.emit.bind(this, 'connection'));
        136
        137 /**
        138 * Starts the server on a random port.
        139 * @return {!webdriver.promise.Promise<Host>} A promise that will resolve
        140 * with the server host when it has fully started.
        141 */
        142 this.start = function() {
        143 if (server.address()) {
        144 return promise.fulfilled(server.address());
        145 }
        146 return portprober.findFreePort('localhost').then(function(port) {
        147 return promise.checkedNodeCall(
        148 server.listen.bind(server, port, 'localhost'));
        149 }).then(function() {
        150 return server.address();
        151 });
        152 };
        153
        154 /**
        155 * Stops the server.
        156 * @return {!webdriver.promise.Promise} A promise that will resolve when the
        157 * server has closed all connections.
        158 */
        159 this.stop = function() {
        160 return new promise.Promise(function(fulfill) {
        161 server.close(fulfill);
        162 });
        163 };
        164
        165 /**
        166 * @return {Host} This server's host info.
        167 * @throws {Error} If the server is not running.
        168 */
        169 this.address = function() {
        170 var addr = server.address();
        171 if (!addr) {
        172 throw Error('There server is not running!');
        173 }
        174 return addr;
        175 };
        176};
        177util.inherits(Server, events.EventEmitter);
        178
        179
        180/**
        181 * @return {!promise.Promise<string>} A promise that will resolve with the path
        182 * to Safari on the current system.
        183 */
        184function findSafariExecutable() {
        185 switch (process.platform) {
        186 case 'darwin':
        187 return promise.fulfilled(
        188 '/Applications/Safari.app/Contents/MacOS/Safari');
        189
        190 case 'win32':
        191 var files = [
        192 process.env['PROGRAMFILES'] || '\\Program Files',
        193 process.env['PROGRAMFILES(X86)'] || '\\Program Files (x86)'
        194 ].map(function(prefix) {
        195 return path.join(prefix, 'Safari\\Safari.exe');
        196 });
        197 return io.exists(files[0]).then(function(exists) {
        198 return exists ? files[0] : io.exists(files[1]).then(function(exists) {
        199 if (exists) {
        200 return files[1];
        201 }
        202 throw Error('Unable to find Safari on the current system');
        203 });
        204 });
        205
        206 default:
        207 return promise.rejected(
        208 Error('Safari is not supported on the current platform: ' +
        209 process.platform));
        210 }
        211}
        212
        213
        214/**
        215 * @param {string} url The URL to connect to.
        216 * @return {!promise.Promise<string>} A promise for the path to a file that
        217 * Safari can open on start-up to trigger a new connection to the WebSocket
        218 * server.
        219 */
        220function createConnectFile(url) {
        221 return io.tmpFile({postfix: '.html'}).then(function(f) {
        222 var writeFile = promise.checkedNodeCall(fs.writeFile,
        223 f,
        224 '<!DOCTYPE html><script>window.location = "' + url + '";</script>',
        225 {encoding: 'utf8'});
        226 return writeFile.then(function() {
        227 return f;
        228 });
        229 });
        230}
        231
        232
        233/**
        234 * Deletes all session data files if so desired.
        235 * @param {!Object} desiredCapabilities .
        236 * @return {!Array<promise.Promise>} A list of promises for the deleted files.
        237 */
        238function cleanSession(desiredCapabilities) {
        239 if (!desiredCapabilities) {
        240 return [];
        241 }
        242 var options = desiredCapabilities[OPTIONS_CAPABILITY_KEY];
        243 if (!options) {
        244 return [];
        245 }
        246 if (!options['cleanSession']) {
        247 return [];
        248 }
        249 return SESSION_DATA_FILES.map(function(file) {
        250 return io.unlink(file);
        251 });
        252}
        253
        254
        255/**
        256 * @constructor
        257 * @implements {webdriver.CommandExecutor}
        258 */
        259var CommandExecutor = function() {
        260 /** @private {Server} */
        261 this.server_ = null;
        262
        263 /** @private {ws.WebSocket} */
        264 this.socket_ = null;
        265
        266 /** @private {promise.Promise.<!exec.Command>} */
        267 this.safari_ = null;
        268};
        269
        270
        271/** @override */
        272CommandExecutor.prototype.execute = function(command, callback) {
        273 var safariCommand = JSON.stringify({
        274 'origin': 'webdriver',
        275 'type': 'command',
        276 'command': {
        277 'id': _base.require('goog.string').getRandomString(),
        278 'name': command.getName(),
        279 'parameters': command.getParameters()
        280 }
        281 });
        282 var self = this;
        283
        284 switch (command.getName()) {
        285 case webdriver.CommandName.NEW_SESSION:
        286 this.startSafari_(command).then(sendCommand, callback);
        287 break;
        288
        289 case webdriver.CommandName.QUIT:
        290 this.destroySession_().then(function() {
        291 callback(null, _base.require('bot.response').createResponse(null));
        292 }, callback);
        293 break;
        294
        295 default:
        296 sendCommand();
        297 break;
        298 }
        299
        300 function sendCommand() {
        301 new promise.Promise(function(fulfill, reject) {
        302 // TODO: support reconnecting with the extension.
        303 if (!self.socket_) {
        304 self.destroySession_().thenFinally(function() {
        305 reject(Error('The connection to the SafariDriver was closed'));
        306 });
        307 return;
        308 }
        309
        310 self.socket_.send(safariCommand, function(err) {
        311 if (err) {
        312 reject(err);
        313 return;
        314 }
        315 });
        316
        317 self.socket_.once('message', function(data) {
        318 try {
        319 data = JSON.parse(data);
        320 } catch (ex) {
        321 reject(Error('Failed to parse driver message: ' + data));
        322 return;
        323 }
        324 fulfill(data['response']);
        325 });
        326
        327 }).then(function(value) {
        328 callback(null, value);
        329 }, callback);
        330 }
        331};
        332
        333
        334/**
        335 * @param {!webdriver.Command} command .
        336 * @private
        337 */
        338CommandExecutor.prototype.startSafari_ = function(command) {
        339 this.server_ = new Server();
        340
        341 this.safari_ = this.server_.start().then(function(address) {
        342 var tasks = cleanSession(command.getParameters()['desiredCapabilities']);
        343 tasks.push(
        344 findSafariExecutable(),
        345 createConnectFile(
        346 'http://' + address.address + ':' + address.port));
        347 return promise.all(tasks).then(function(tasks) {
        348 var exe = tasks[tasks.length - 2];
        349 var html = tasks[tasks.length - 1];
        350 return exec(exe, {args: [html]});
        351 });
        352 });
        353
        354 var connected = promise.defer();
        355 var self = this;
        356 var start = Date.now();
        357 var timer = setTimeout(function() {
        358 connected.reject(Error(
        359 'Failed to connect to the SafariDriver after ' + (Date.now() - start) +
        360 ' ms; Have you installed the latest extension from ' +
        361 'http://selenium-release.storage.googleapis.com/index.html?'));
        362 }, 10 * 1000);
        363 this.server_.once('connection', function(socket) {
        364 clearTimeout(timer);
        365 self.socket_ = socket;
        366 socket.once('close', function() {
        367 self.socket_ = null;
        368 });
        369 connected.fulfill();
        370 });
        371 return connected.promise;
        372};
        373
        374
        375/**
        376 * Destroys the active session by stopping the WebSocket server and killing the
        377 * Safari subprocess.
        378 * @private
        379 */
        380CommandExecutor.prototype.destroySession_ = function() {
        381 var tasks = [];
        382 if (this.server_) {
        383 tasks.push(this.server_.stop());
        384 }
        385 if (this.safari_) {
        386 tasks.push(this.safari_.then(function(safari) {
        387 safari.kill();
        388 return safari.result();
        389 }));
        390 }
        391 var self = this;
        392 return promise.all(tasks).thenFinally(function() {
        393 self.server_ = null;
        394 self.socket_ = null;
        395 self.safari_ = null;
        396 });
        397};
        398
        399
        400/** @const */
        401var OPTIONS_CAPABILITY_KEY = 'safari.options';
        402
        403
        404
        405/**
        406 * Configuration options specific to the {@link Driver SafariDriver}.
        407 * @constructor
        408 * @extends {webdriver.Serializable}
        409 */
        410var Options = function() {
        411 webdriver.Serializable.call(this);
        412
        413 /** @private {Object<string, *>} */
        414 this.options_ = null;
        415
        416 /** @private {webdriver.logging.Preferences} */
        417 this.logPrefs_ = null;
        418};
        419util.inherits(Options, webdriver.Serializable);
        420
        421
        422/**
        423 * Extracts the SafariDriver specific options from the given capabilities
        424 * object.
        425 * @param {!webdriver.Capabilities} capabilities The capabilities object.
        426 * @return {!Options} The ChromeDriver options.
        427 */
        428Options.fromCapabilities = function(capabilities) {
        429 var options = new Options();
        430
        431 var o = capabilities.get(OPTIONS_CAPABILITY_KEY);
        432 if (o instanceof Options) {
        433 options = o;
        434 } else if (o) {
        435 options.setCleanSession(o.cleanSession);
        436 }
        437
        438 if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) {
        439 options.setLoggingPrefs(
        440 capabilities.get(webdriver.Capability.LOGGING_PREFS));
        441 }
        442
        443 return options;
        444};
        445
        446
        447/**
        448 * Sets whether to force Safari to start with a clean session. Enabling this
        449 * option will cause all global browser data to be deleted.
        450 * @param {boolean} clean Whether to make sure the session has no cookies,
        451 * cache entries, local storage, or databases.
        452 * @return {!Options} A self reference.
        453 */
        454Options.prototype.setCleanSession = function(clean) {
        455 if (!this.options_) {
        456 this.options_ = {};
        457 }
        458 this.options_['cleanSession'] = clean;
        459 return this;
        460};
        461
        462
        463/**
        464 * Sets the logging preferences for the new session.
        465 * @param {!webdriver.logging.Preferences} prefs The logging preferences.
        466 * @return {!Options} A self reference.
        467 */
        468Options.prototype.setLoggingPrefs = function(prefs) {
        469 this.logPrefs_ = prefs;
        470 return this;
        471};
        472
        473
        474/**
        475 * Converts this options instance to a {@link webdriver.Capabilities} object.
        476 * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge
        477 * these options into, if any.
        478 * @return {!webdriver.Capabilities} The capabilities.
        479 */
        480Options.prototype.toCapabilities = function(opt_capabilities) {
        481 var capabilities = opt_capabilities || webdriver.Capabilities.safari();
        482 if (this.logPrefs_) {
        483 capabilities.set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_);
        484 }
        485 if (this.options_) {
        486 capabilities.set(OPTIONS_CAPABILITY_KEY, this);
        487 }
        488 return capabilities;
        489};
        490
        491
        492/**
        493 * Converts this instance to its JSON wire protocol representation. Note this
        494 * function is an implementation detail not intended for general use.
        495 * @return {!Object<string, *>} The JSON wire protocol representation of this
        496 * instance.
        497 * @override
        498 */
        499Options.prototype.serialize = function() {
        500 return this.options_ || {};
        501};
        502
        503
        504
        505/**
        506 * A WebDriver client for Safari. This class should never be instantiated
        507 * directly; instead, use the {@link selenium-webdriver.Builder}:
        508 *
        509 * var driver = new Builder()
        510 * .forBrowser('safari')
        511 * .build();
        512 *
        513 * @param {(Options|webdriver.Capabilities)=} opt_config The configuration
        514 * options for the new session.
        515 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to create
        516 * the driver under.
        517 * @constructor
        518 * @extends {webdriver.WebDriver}
        519 */
        520var Driver = function(opt_config, opt_flow) {
        521 var executor = new CommandExecutor();
        522 var capabilities =
        523 opt_config instanceof Options ? opt_config.toCapabilities() :
        524 (opt_config || webdriver.Capabilities.safari());
        525
        526 var driver = webdriver.WebDriver.createSession(
        527 executor, capabilities, opt_flow);
        528 webdriver.WebDriver.call(
        529 this, driver.getSession(), executor, driver.controlFlow());
        530};
        531util.inherits(Driver, webdriver.WebDriver);
        532
        533
        534// Public API
        535
        536
        537exports.Driver = Driver;
        538exports.Options = Options;
        \ No newline at end of file diff --git a/docs/source/testing/assert.js.src.html b/docs/source/testing/assert.js.src.html index 125141d..48bfc89 100644 --- a/docs/source/testing/assert.js.src.html +++ b/docs/source/testing/assert.js.src.html @@ -1 +1 @@ -assert.js

        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
        35var base = require('../_base'),
        36 assert = base.require('webdriver.testing.assert');
        37
        38
        39// PUBLIC API
        40
        41
        42/** @type {webdriver.testing.assert.} */
        43module.exports = assert;
        \ No newline at end of file +assert.js

        testing/assert.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Defines a library that simplifies writing assertions against
        20 * promised values.
        21 *
        22 * > <hr>
        23 * > __NOTE:__ This module is considered experimental and is subject to
        24 * > change, or removal, at any time!
        25 * > <hr>
        26 *
        27 * Sample usage:
        28 *
        29 * var driver = new webdriver.Builder().build();
        30 * driver.get('http://www.google.com');
        31 *
        32 * assert(driver.getTitle()).equalTo('Google');
        33 */
        34
        35var base = require('../_base'),
        36 assert = base.require('webdriver.testing.assert');
        37
        38
        39// PUBLIC API
        40
        41
        42/**
        43 * Creates a new assertion.
        44 * @param {*} value The value to perform an assertion on.
        45 * @return {!webdriver.testing.Assertion} The new assertion.
        46 */
        47module.exports = function(value) {
        48 return assert(value);
        49};
        50
        51
        52/**
        53 * Registers a new assertion to expose from the
        54 * {@link webdriver.testing.Assertion} prototype.
        55 * @param {string} name The assertion name.
        56 * @param {(function(new: goog.labs.testing.Matcher, *)|
        57 * {matches: function(*): boolean,
        58 * describe: function(): string})} matcherTemplate Either the
        59 * matcher constructor to use, or an object literal defining a matcher.
        60 */
        61module.exports.register = assert.register;
        \ No newline at end of file diff --git a/docs/source/testing/index.js.src.html b/docs/source/testing/index.js.src.html index 5049609..81d7f5a 100644 --- a/docs/source/testing/index.js.src.html +++ b/docs/source/testing/index.js.src.html @@ -1 +1 @@ -index.js

        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 webdriver.promise.ControlFlow to
        31 * simplify writing asynchronous tests:
        32 * <pre><code>
        33 * var webdriver = require('selenium-webdriver'),
        34 * remote = require('selenium-webdriver/remote'),
        35 * test = require('selenium-webdriver/testing');
        36 *
        37 * test.describe('Google Search', function() {
        38 * var driver, server;
        39 *
        40 * test.before(function() {
        41 * server = new remote.SeleniumServer({
        42 * jar: 'path/to/selenium-server-standalone.jar'
        43 * });
        44 * server.start();
        45 *
        46 * driver = new webdriver.Builder().
        47 * withCapabilities({'browserName': 'firefox'}).
        48 * usingServer(server.address()).
        49 * build();
        50 * });
        51 *
        52 * test.after(function() {
        53 * driver.quit();
        54 * server.stop();
        55 * });
        56 *
        57 * test.it('should append query to title', function() {
        58 * driver.get('http://www.google.com');
        59 * driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
        60 * driver.findElement(webdriver.By.name('btnG')).click();
        61 * driver.wait(function() {
        62 * return driver.getTitle().then(function(title) {
        63 * return 'webdriver - Google Search' === title;
        64 * });
        65 * }, 1000, 'Waiting for title to update');
        66 * });
        67 * });
        68 * </code></pre>
        69 *
        70 * <p>You may conditionally suppress a test function using the exported
        71 * "ignore" function. If the provided predicate returns true, the attached
        72 * test case will be skipped:
        73 * <pre><code>
        74 * test.ignore(maybe()).it('is flaky', function() {
        75 * if (Math.random() < 0.5) throw Error();
        76 * });
        77 *
        78 * function maybe() { return Math.random() < 0.5; }
        79 * </code></pre>
        80 */
        81
        82var flow = require('..').promise.controlFlow();
        83
        84
        85/**
        86 * Wraps a function so that all passed arguments are ignored.
        87 * @param {!Function} fn The function to wrap.
        88 * @return {!Function} The wrapped function.
        89 */
        90function seal(fn) {
        91 return function() {
        92 fn();
        93 };
        94}
        95
        96
        97/**
        98 * Wraps a function on Mocha's BDD interface so it runs inside a
        99 * webdriver.promise.ControlFlow and waits for the flow to complete before
        100 * continuing.
        101 * @param {!Function} globalFn The function to wrap.
        102 * @return {!Function} The new function.
        103 */
        104function wrapped(globalFn) {
        105 return function() {
        106 switch (arguments.length) {
        107 case 1:
        108 globalFn(asyncTestFn(arguments[0]));
        109 break;
        110
        111 case 2:
        112 globalFn(arguments[0], asyncTestFn(arguments[1]));
        113 break;
        114
        115 default:
        116 throw Error('Invalid # arguments: ' + arguments.length);
        117 }
        118 };
        119
        120 function asyncTestFn(fn) {
        121 return function(done) {
        122 this.timeout(0);
        123 var timeout = this.timeout;
        124 this.timeout = undefined; // Do not let tests change the timeout.
        125 try {
        126 flow.execute(fn.bind(this)).then(seal(done), done);
        127 } finally {
        128 this.timeout = timeout;
        129 }
        130 };
        131 }
        132}
        133
        134
        135/**
        136 * Ignores the test chained to this function if the provided predicate returns
        137 * true.
        138 * @param {function(): boolean} predicateFn A predicate to call to determine
        139 * if the test should be suppressed. This function MUST be synchronous.
        140 * @return {!Object} An object with wrapped versions of {@link #it()} and
        141 * {@link #describe()} that ignore tests as indicated by the predicate.
        142 */
        143function ignore(predicateFn) {
        144 var describe = wrap(exports.xdescribe, exports.describe);
        145 describe.only = wrap(exports.xdescribe, exports.describe.only);
        146
        147 var it = wrap(exports.xit, exports.it);
        148 it.only = wrap(exports.xit, exports.it.only);
        149
        150 return {
        151 describe: describe,
        152 it: it
        153 };
        154
        155 function wrap(onSkip, onRun) {
        156 return function(title, fn) {
        157 if (predicateFn()) {
        158 onSkip(title, fn);
        159 } else {
        160 onRun(title, fn);
        161 }
        162 };
        163 }
        164}
        165
        166
        167// PUBLIC API
        168
        169/**
        170 * Registers a new test suite.
        171 * @param {string} name The suite name.
        172 * @param {function()=} fn The suite function, or {@code undefined} to define
        173 * a pending test suite.
        174 */
        175exports.describe = global.describe;
        176
        177/**
        178 * Defines a suppressed test suite.
        179 * @param {string} name The suite name.
        180 * @param {function()=} fn The suite function, or {@code undefined} to define
        181 * a pending test suite.
        182 */
        183exports.xdescribe = global.xdescribe;
        184exports.describe.skip = global.describe.skip;
        185
        186/**
        187 * Register a function to call after the current suite finishes.
        188 * @param {function()} fn .
        189 */
        190exports.after = wrapped(global.after);
        191
        192/**
        193 * Register a function to call after each test in a suite.
        194 * @param {function()} fn .
        195 */
        196exports.afterEach = wrapped(global.afterEach);
        197
        198/**
        199 * Register a function to call before the current suite starts.
        200 * @param {function()} fn .
        201 */
        202exports.before = wrapped(global.before);
        203
        204/**
        205 * Register a function to call before each test in a suite.
        206 * @param {function()} fn .
        207 */
        208exports.beforeEach = wrapped(global.beforeEach);
        209
        210/**
        211 * Add a test to the current suite.
        212 * @param {string} name The test name.
        213 * @param {function()=} fn The test function, or {@code undefined} to define
        214 * a pending test case.
        215 */
        216exports.it = wrapped(global.it);
        217
        218/**
        219 * An alias for {@link #it()} that flags the test as the only one that should
        220 * be run within the current suite.
        221 * @param {string} name The test name.
        222 * @param {function()=} fn The test function, or {@code undefined} to define
        223 * a pending test case.
        224 */
        225exports.iit = exports.it.only = wrapped(global.it.only);
        226
        227/**
        228 * Adds a test to the current suite while suppressing it so it is not run.
        229 * @param {string} name The test name.
        230 * @param {function()=} fn The test function, or {@code undefined} to define
        231 * a pending test case.
        232 */
        233exports.xit = exports.it.skip = wrapped(global.xit);
        234
        235exports.ignore = ignore;
        \ No newline at end of file +index.js

        testing/index.js

        1// Licensed to the Software Freedom Conservancy (SFC) under one
        2// or more contributor license agreements. See the NOTICE file
        3// distributed with this work for additional information
        4// regarding copyright ownership. The SFC licenses this file
        5// to you under the Apache License, Version 2.0 (the
        6// "License"); you may not use this file except in compliance
        7// with the License. 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,
        12// software distributed under the License is distributed on an
        13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
        14// KIND, either express or implied. See the License for the
        15// specific language governing permissions and limitations
        16// under the License.
        17
        18/**
        19 * @fileoverview Provides wrappers around the following global functions from
        20 * [Mocha's BDD interface](https://github.com/mochajs/mocha):
        21 *
        22 * - after
        23 * - afterEach
        24 * - before
        25 * - beforeEach
        26 * - it
        27 * - it.only
        28 * - it.skip
        29 * - xit
        30 *
        31 * The provided wrappers leverage the {@link webdriver.promise.ControlFlow}
        32 * to simplify writing asynchronous tests:
        33 *
        34 * var By = require('selenium-webdriver').By,
        35 * until = require('selenium-webdriver').until,
        36 * firefox = require('selenium-webdriver/firefox'),
        37 * test = require('selenium-webdriver/testing');
        38 *
        39 * test.describe('Google Search', function() {
        40 * var driver;
        41 *
        42 * test.before(function() {
        43 * driver = new firefox.Driver();
        44 * });
        45 *
        46 * test.after(function() {
        47 * driver.quit();
        48 * });
        49 *
        50 * test.it('should append query to title', function() {
        51 * driver.get('http://www.google.com/ncr');
        52 * driver.findElement(By.name('q')).sendKeys('webdriver');
        53 * driver.findElement(By.name('btnG')).click();
        54 * driver.wait(until.titleIs('webdriver - Google Search'), 1000);
        55 * });
        56 * });
        57 *
        58 * You may conditionally suppress a test function using the exported
        59 * "ignore" function. If the provided predicate returns true, the attached
        60 * test case will be skipped:
        61 *
        62 * test.ignore(maybe()).it('is flaky', function() {
        63 * if (Math.random() < 0.5) throw Error();
        64 * });
        65 *
        66 * function maybe() { return Math.random() < 0.5; }
        67 */
        68
        69var promise = require('..').promise;
        70var flow = promise.controlFlow();
        71
        72
        73/**
        74 * Wraps a function so that all passed arguments are ignored.
        75 * @param {!Function} fn The function to wrap.
        76 * @return {!Function} The wrapped function.
        77 */
        78function seal(fn) {
        79 return function() {
        80 fn();
        81 };
        82}
        83
        84
        85/**
        86 * Wraps a function on Mocha's BDD interface so it runs inside a
        87 * webdriver.promise.ControlFlow and waits for the flow to complete before
        88 * continuing.
        89 * @param {!Function} globalFn The function to wrap.
        90 * @return {!Function} The new function.
        91 */
        92function wrapped(globalFn) {
        93 return function() {
        94 if (arguments.length === 1) {
        95 return globalFn(makeAsyncTestFn(arguments[0]));
        96 }
        97 else if (arguments.length === 2) {
        98 return globalFn(arguments[0], makeAsyncTestFn(arguments[1]));
        99 }
        100 else {
        101 throw Error('Invalid # arguments: ' + arguments.length);
        102 }
        103 };
        104}
        105
        106/**
        107 * Make a wrapper to invoke caller's test function, fn. Run the test function
        108 * within a ControlFlow.
        109 *
        110 * Should preserve the semantics of Mocha's Runnable.prototype.run (See
        111 * https://github.com/mochajs/mocha/blob/master/lib/runnable.js#L192)
        112 *
        113 * @param {Function} fn
        114 * @return {Function}
        115 */
        116function makeAsyncTestFn(fn) {
        117 var async = fn.length > 0; // if test function expects a callback, its "async"
        118
        119 var ret = function(done) {
        120 var runnable = this.runnable();
        121 var mochaCallback = runnable.callback;
        122 runnable.callback = function() {
        123 flow.reset();
        124 return mochaCallback.apply(this, arguments);
        125 };
        126
        127 var testFn = fn.bind(this);
        128 flow.execute(function controlFlowExecute() {
        129 return new promise.Promise(function(fulfill, reject) {
        130 if (async) {
        131 // If testFn is async (it expects a done callback), resolve the promise of this
        132 // test whenever that callback says to. Any promises returned from testFn are
        133 // ignored.
        134 testFn(function testFnDoneCallback(err) {
        135 if (err) {
        136 reject(err);
        137 } else {
        138 fulfill();
        139 }
        140 });
        141 } else {
        142 // Without a callback, testFn can return a promise, or it will
        143 // be assumed to have completed synchronously
        144 fulfill(testFn());
        145 }
        146 }, flow);
        147 }, runnable.fullTitle()).then(seal(done), done);
        148 };
        149
        150 ret.toString = function() {
        151 return fn.toString();
        152 };
        153
        154 return ret;
        155}
        156
        157
        158/**
        159 * Ignores the test chained to this function if the provided predicate returns
        160 * true.
        161 * @param {function(): boolean} predicateFn A predicate to call to determine
        162 * if the test should be suppressed. This function MUST be synchronous.
        163 * @return {!Object} An object with wrapped versions of {@link #it()} and
        164 * {@link #describe()} that ignore tests as indicated by the predicate.
        165 */
        166function ignore(predicateFn) {
        167 var describe = wrap(exports.xdescribe, exports.describe);
        168 describe.only = wrap(exports.xdescribe, exports.describe.only);
        169
        170 var it = wrap(exports.xit, exports.it);
        171 it.only = wrap(exports.xit, exports.it.only);
        172
        173 return {
        174 describe: describe,
        175 it: it
        176 };
        177
        178 function wrap(onSkip, onRun) {
        179 return function(title, fn) {
        180 if (predicateFn()) {
        181 onSkip(title, fn);
        182 } else {
        183 onRun(title, fn);
        184 }
        185 };
        186 }
        187}
        188
        189
        190// PUBLIC API
        191
        192/**
        193 * Registers a new test suite.
        194 * @param {string} name The suite name.
        195 * @param {function()=} fn The suite function, or {@code undefined} to define
        196 * a pending test suite.
        197 */
        198exports.describe = global.describe;
        199
        200/**
        201 * Defines a suppressed test suite.
        202 * @param {string} name The suite name.
        203 * @param {function()=} fn The suite function, or {@code undefined} to define
        204 * a pending test suite.
        205 */
        206exports.xdescribe = global.xdescribe;
        207exports.describe.skip = global.describe.skip;
        208
        209/**
        210 * Register a function to call after the current suite finishes.
        211 * @param {function()} fn .
        212 */
        213exports.after = wrapped(global.after);
        214
        215/**
        216 * Register a function to call after each test in a suite.
        217 * @param {function()} fn .
        218 */
        219exports.afterEach = wrapped(global.afterEach);
        220
        221/**
        222 * Register a function to call before the current suite starts.
        223 * @param {function()} fn .
        224 */
        225exports.before = wrapped(global.before);
        226
        227/**
        228 * Register a function to call before each test in a suite.
        229 * @param {function()} fn .
        230 */
        231exports.beforeEach = wrapped(global.beforeEach);
        232
        233/**
        234 * Add a test to the current suite.
        235 * @param {string} name The test name.
        236 * @param {function()=} fn The test function, or {@code undefined} to define
        237 * a pending test case.
        238 */
        239exports.it = wrapped(global.it);
        240
        241/**
        242 * An alias for {@link #it()} that flags the test as the only one that should
        243 * be run within the current suite.
        244 * @param {string} name The test name.
        245 * @param {function()=} fn The test function, or {@code undefined} to define
        246 * a pending test case.
        247 */
        248exports.iit = exports.it.only = wrapped(global.it.only);
        249
        250/**
        251 * Adds a test to the current suite while suppressing it so it is not run.
        252 * @param {string} name The test name.
        253 * @param {function()=} fn The test function, or {@code undefined} to define
        254 * a pending test case.
        255 */
        256exports.xit = exports.it.skip = wrapped(global.xit);
        257
        258exports.ignore = ignore;
        \ No newline at end of file diff --git a/docs/types.js b/docs/types.js index 88ff729..9eab2ee 100644 --- a/docs/types.js +++ b/docs/types.js @@ -1 +1 @@ -var TYPES = {"files":[{"name":"_base.js","href":"source/_base.js.src.html"},{"name":"builder.js","href":"source/builder.js.src.html"},{"name":"chrome.js","href":"source/chrome.js.src.html"},{"name":"error.js","href":"source/error.js.src.html"},{"name":"executors.js","href":"source/executors.js.src.html"},{"name":"http/index.js","href":"source/http/index.js.src.html"},{"name":"http/util.js","href":"source/http/util.js.src.html"},{"name":"index.js","href":"source/index.js.src.html"},{"name":"io/index.js","href":"source/io/index.js.src.html"},{"name":"lib/atoms/error.js","href":"source/lib/atoms/error.js.src.html"},{"name":"lib/atoms/json.js","href":"source/lib/atoms/json.js.src.html"},{"name":"lib/atoms/response.js","href":"source/lib/atoms/response.js.src.html"},{"name":"lib/atoms/userAgent.js","href":"source/lib/atoms/userAgent.js.src.html"},{"name":"lib/goog/array/array.js","href":"source/lib/goog/array/array.js.src.html"},{"name":"lib/goog/asserts/asserts.js","href":"source/lib/goog/asserts/asserts.js.src.html"},{"name":"lib/goog/base.js","href":"source/lib/goog/base.js.src.html"},{"name":"lib/goog/debug/error.js","href":"source/lib/goog/debug/error.js.src.html"},{"name":"lib/goog/deps.js","href":"source/lib/goog/deps.js.src.html"},{"name":"lib/goog/iter/iter.js","href":"source/lib/goog/iter/iter.js.src.html"},{"name":"lib/goog/json/json.js","href":"source/lib/goog/json/json.js.src.html"},{"name":"lib/goog/labs/testing/assertthat.js","href":"source/lib/goog/labs/testing/assertthat.js.src.html"},{"name":"lib/goog/labs/testing/logicmatcher.js","href":"source/lib/goog/labs/testing/logicmatcher.js.src.html"},{"name":"lib/goog/labs/testing/matcher.js","href":"source/lib/goog/labs/testing/matcher.js.src.html"},{"name":"lib/goog/labs/testing/numbermatcher.js","href":"source/lib/goog/labs/testing/numbermatcher.js.src.html"},{"name":"lib/goog/labs/testing/objectmatcher.js","href":"source/lib/goog/labs/testing/objectmatcher.js.src.html"},{"name":"lib/goog/labs/testing/stringmatcher.js","href":"source/lib/goog/labs/testing/stringmatcher.js.src.html"},{"name":"lib/goog/net/wrapperxmlhttpfactory.js","href":"source/lib/goog/net/wrapperxmlhttpfactory.js.src.html"},{"name":"lib/goog/net/xmlhttp.js","href":"source/lib/goog/net/xmlhttp.js.src.html"},{"name":"lib/goog/net/xmlhttpfactory.js","href":"source/lib/goog/net/xmlhttpfactory.js.src.html"},{"name":"lib/goog/object/object.js","href":"source/lib/goog/object/object.js.src.html"},{"name":"lib/goog/string/string.js","href":"source/lib/goog/string/string.js.src.html"},{"name":"lib/goog/structs/map.js","href":"source/lib/goog/structs/map.js.src.html"},{"name":"lib/goog/structs/structs.js","href":"source/lib/goog/structs/structs.js.src.html"},{"name":"lib/goog/uri/uri.js","href":"source/lib/goog/uri/uri.js.src.html"},{"name":"lib/goog/uri/utils.js","href":"source/lib/goog/uri/utils.js.src.html"},{"name":"lib/goog/useragent/product.js","href":"source/lib/goog/useragent/product.js.src.html"},{"name":"lib/goog/useragent/product_isversion.js","href":"source/lib/goog/useragent/product_isversion.js.src.html"},{"name":"lib/goog/useragent/useragent.js","href":"source/lib/goog/useragent/useragent.js.src.html"},{"name":"lib/webdriver/abstractbuilder.js","href":"source/lib/webdriver/abstractbuilder.js.src.html"},{"name":"lib/webdriver/actionsequence.js","href":"source/lib/webdriver/actionsequence.js.src.html"},{"name":"lib/webdriver/builder.js","href":"source/lib/webdriver/builder.js.src.html"},{"name":"lib/webdriver/button.js","href":"source/lib/webdriver/button.js.src.html"},{"name":"lib/webdriver/capabilities.js","href":"source/lib/webdriver/capabilities.js.src.html"},{"name":"lib/webdriver/command.js","href":"source/lib/webdriver/command.js.src.html"},{"name":"lib/webdriver/events.js","href":"source/lib/webdriver/events.js.src.html"},{"name":"lib/webdriver/firefoxdomexecutor.js","href":"source/lib/webdriver/firefoxdomexecutor.js.src.html"},{"name":"lib/webdriver/http/corsclient.js","href":"source/lib/webdriver/http/corsclient.js.src.html"},{"name":"lib/webdriver/http/http.js","href":"source/lib/webdriver/http/http.js.src.html"},{"name":"lib/webdriver/http/xhrclient.js","href":"source/lib/webdriver/http/xhrclient.js.src.html"},{"name":"lib/webdriver/key.js","href":"source/lib/webdriver/key.js.src.html"},{"name":"lib/webdriver/locators.js","href":"source/lib/webdriver/locators.js.src.html"},{"name":"lib/webdriver/logging.js","href":"source/lib/webdriver/logging.js.src.html"},{"name":"lib/webdriver/process.js","href":"source/lib/webdriver/process.js.src.html"},{"name":"lib/webdriver/promise.js","href":"source/lib/webdriver/promise.js.src.html"},{"name":"lib/webdriver/session.js","href":"source/lib/webdriver/session.js.src.html"},{"name":"lib/webdriver/stacktrace.js","href":"source/lib/webdriver/stacktrace.js.src.html"},{"name":"lib/webdriver/testing/asserts.js","href":"source/lib/webdriver/testing/asserts.js.src.html"},{"name":"lib/webdriver/webdriver.js","href":"source/lib/webdriver/webdriver.js.src.html"},{"name":"net/index.js","href":"source/net/index.js.src.html"},{"name":"net/portprober.js","href":"source/net/portprober.js.src.html"},{"name":"phantomjs.js","href":"source/phantomjs.js.src.html"},{"name":"proxy.js","href":"source/proxy.js.src.html"},{"name":"remote/index.js","href":"source/remote/index.js.src.html"},{"name":"testing/assert.js","href":"source/testing/assert.js.src.html"},{"name":"testing/index.js","href":"source/testing/index.js.src.html"}],"types":[{"isInterface":false,"name":"bot","isTypedef":false,"href":"namespace_bot.html"},{"isInterface":false,"name":"bot.Error","isTypedef":false,"href":"class_bot_Error.html"},{"isInterface":false,"name":"bot.Error.State","isTypedef":false,"href":"enum_bot_Error_State.html"},{"isInterface":false,"name":"bot.ErrorCode","isTypedef":false,"href":"enum_bot_ErrorCode.html"},{"isInterface":false,"name":"bot.json","isTypedef":false,"href":"namespace_bot_json.html"},{"isInterface":false,"name":"bot.response","isTypedef":false,"href":"namespace_bot_response.html"},{"name":"bot.response.ResponseObject","isTypedef":true,"href":"namespace_bot_response.html#bot.response.ResponseObject"},{"isInterface":false,"name":"bot.userAgent","isTypedef":false,"href":"namespace_bot_userAgent.html"},{"isInterface":false,"name":"goog","isTypedef":false,"href":"namespace_goog.html"},{"isInterface":false,"name":"goog.Uri","isTypedef":false,"href":"class_goog_Uri.html"},{"isInterface":false,"name":"goog.Uri.QueryData","isTypedef":false,"href":"class_goog_Uri_QueryData.html"},{"isInterface":false,"name":"goog.array","isTypedef":false,"href":"namespace_goog_array.html"},{"name":"goog.array.ArrayLike","isTypedef":true,"href":"namespace_goog_array.html#goog.array.ArrayLike"},{"isInterface":false,"name":"goog.asserts","isTypedef":false,"href":"namespace_goog_asserts.html"},{"isInterface":false,"name":"goog.asserts.AssertionError","isTypedef":false,"href":"class_goog_asserts_AssertionError.html"},{"isInterface":false,"name":"goog.debug","isTypedef":false,"href":"namespace_goog_debug.html"},{"isInterface":false,"name":"goog.debug.Error","isTypedef":false,"href":"class_goog_debug_Error.html"},{"isInterface":false,"name":"goog.iter","isTypedef":false,"href":"namespace_goog_iter.html"},{"name":"goog.iter.Iterable","isTypedef":true,"href":"namespace_goog_iter.html#goog.iter.Iterable"},{"isInterface":false,"name":"goog.iter.Iterator","isTypedef":false,"href":"class_goog_iter_Iterator.html"},{"isInterface":false,"name":"goog.json","isTypedef":false,"href":"namespace_goog_json.html"},{"name":"goog.json.Replacer","isTypedef":true,"href":"namespace_goog_json.html#goog.json.Replacer"},{"name":"goog.json.Reviver","isTypedef":true,"href":"namespace_goog_json.html#goog.json.Reviver"},{"isInterface":false,"name":"goog.json.Serializer","isTypedef":false,"href":"class_goog_json_Serializer.html"},{"isInterface":false,"name":"goog.labs","isTypedef":false,"href":"namespace_goog_labs.html"},{"isInterface":false,"name":"goog.labs.testing","isTypedef":false,"href":"namespace_goog_labs_testing.html"},{"isInterface":false,"name":"goog.labs.testing.AllOfMatcher","isTypedef":false,"href":"class_goog_labs_testing_AllOfMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.AnyOfMatcher","isTypedef":false,"href":"class_goog_labs_testing_AnyOfMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.CloseToMatcher","isTypedef":false,"href":"class_goog_labs_testing_CloseToMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.ContainsStringMatcher","isTypedef":false,"href":"class_goog_labs_testing_ContainsStringMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.EndsWithMatcher","isTypedef":false,"href":"class_goog_labs_testing_EndsWithMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.EqualToIgnoringWhitespaceMatcher","isTypedef":false,"href":"class_goog_labs_testing_EqualToIgnoringWhitespaceMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.EqualToMatcher","isTypedef":false,"href":"class_goog_labs_testing_EqualToMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.EqualsMatcher","isTypedef":false,"href":"class_goog_labs_testing_EqualsMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.GreaterThanEqualToMatcher","isTypedef":false,"href":"class_goog_labs_testing_GreaterThanEqualToMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.GreaterThanMatcher","isTypedef":false,"href":"class_goog_labs_testing_GreaterThanMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.HasPropertyMatcher","isTypedef":false,"href":"class_goog_labs_testing_HasPropertyMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.InstanceOfMatcher","isTypedef":false,"href":"class_goog_labs_testing_InstanceOfMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.IsNotMatcher","isTypedef":false,"href":"class_goog_labs_testing_IsNotMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.IsNullMatcher","isTypedef":false,"href":"class_goog_labs_testing_IsNullMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.IsNullOrUndefinedMatcher","isTypedef":false,"href":"class_goog_labs_testing_IsNullOrUndefinedMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.IsUndefinedMatcher","isTypedef":false,"href":"class_goog_labs_testing_IsUndefinedMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.LessThanEqualToMatcher","isTypedef":false,"href":"class_goog_labs_testing_LessThanEqualToMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.LessThanMatcher","isTypedef":false,"href":"class_goog_labs_testing_LessThanMatcher.html"},{"isInterface":true,"name":"goog.labs.testing.Matcher","isTypedef":false,"href":"interface_goog_labs_testing_Matcher.html"},{"isInterface":false,"name":"goog.labs.testing.MatcherError","isTypedef":false,"href":"class_goog_labs_testing_MatcherError.html"},{"isInterface":false,"name":"goog.labs.testing.ObjectEqualsMatcher","isTypedef":false,"href":"class_goog_labs_testing_ObjectEqualsMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.RegexMatcher","isTypedef":false,"href":"class_goog_labs_testing_RegexMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.StartsWithMatcher","isTypedef":false,"href":"class_goog_labs_testing_StartsWithMatcher.html"},{"isInterface":false,"name":"goog.labs.testing.StringContainsInOrderMatcher","isTypedef":false,"href":"class_goog_labs_testing_StringContainsInOrderMatcher.html"},{"isInterface":false,"name":"goog.net","isTypedef":false,"href":"namespace_goog_net.html"},{"isInterface":false,"name":"goog.net.DefaultXmlHttpFactory","isTypedef":false,"href":"class_goog_net_DefaultXmlHttpFactory.html"},{"isInterface":false,"name":"goog.net.WrapperXmlHttpFactory","isTypedef":false,"href":"class_goog_net_WrapperXmlHttpFactory.html"},{"isInterface":false,"name":"goog.net.XmlHttp","isTypedef":false,"href":"namespace_goog_net_XmlHttp.html"},{"isInterface":false,"name":"goog.net.XmlHttp.OptionType","isTypedef":false,"href":"enum_goog_net_XmlHttp_OptionType.html"},{"isInterface":false,"name":"goog.net.XmlHttp.ReadyState","isTypedef":false,"href":"enum_goog_net_XmlHttp_ReadyState.html"},{"isInterface":false,"name":"goog.net.XmlHttpFactory","isTypedef":false,"href":"class_goog_net_XmlHttpFactory.html"},{"isInterface":false,"name":"goog.object","isTypedef":false,"href":"namespace_goog_object.html"},{"isInterface":false,"name":"goog.string","isTypedef":false,"href":"namespace_goog_string.html"},{"isInterface":false,"name":"goog.string.Unicode","isTypedef":false,"href":"enum_goog_string_Unicode.html"},{"isInterface":false,"name":"goog.structs","isTypedef":false,"href":"namespace_goog_structs.html"},{"isInterface":false,"name":"goog.structs.Map","isTypedef":false,"href":"class_goog_structs_Map.html"},{"isInterface":false,"name":"goog.uri","isTypedef":false,"href":"namespace_goog_uri.html"},{"isInterface":false,"name":"goog.uri.utils","isTypedef":false,"href":"namespace_goog_uri_utils.html"},{"name":"goog.uri.utils.QueryArray","isTypedef":true,"href":"namespace_goog_uri_utils.html#goog.uri.utils.QueryArray"},{"name":"goog.uri.utils.QueryValue","isTypedef":true,"href":"namespace_goog_uri_utils.html#goog.uri.utils.QueryValue"},{"isInterface":false,"name":"goog.uri.utils.CharCode_","isTypedef":false,"href":"enum_goog_uri_utils_CharCode_.html"},{"isInterface":false,"name":"goog.uri.utils.ComponentIndex","isTypedef":false,"href":"enum_goog_uri_utils_ComponentIndex.html"},{"isInterface":false,"name":"goog.uri.utils.StandardQueryParam","isTypedef":false,"href":"enum_goog_uri_utils_StandardQueryParam.html"},{"isInterface":false,"name":"goog.userAgent","isTypedef":false,"href":"namespace_goog_userAgent.html"},{"isInterface":false,"name":"goog.userAgent.product","isTypedef":false,"href":"namespace_goog_userAgent_product.html"},{"isInterface":false,"name":"webdriver","isTypedef":false,"href":"namespace_webdriver.html"},{"isInterface":false,"name":"webdriver.AbstractBuilder","isTypedef":false,"href":"class_webdriver_AbstractBuilder.html"},{"isInterface":false,"name":"webdriver.ActionSequence","isTypedef":false,"href":"class_webdriver_ActionSequence.html"},{"isInterface":false,"name":"webdriver.Alert","isTypedef":false,"href":"class_webdriver_Alert.html"},{"isInterface":false,"name":"webdriver.Browser","isTypedef":false,"href":"enum_webdriver_Browser.html"},{"isInterface":false,"name":"webdriver.Builder","isTypedef":false,"href":"class_webdriver_Builder.html"},{"isInterface":false,"name":"webdriver.Button","isTypedef":false,"href":"enum_webdriver_Button.html"},{"isInterface":false,"name":"webdriver.By","isTypedef":false,"href":"namespace_webdriver_By.html"},{"name":"webdriver.By.Hash","isTypedef":true,"href":"namespace_webdriver_By.html#webdriver.By.Hash"},{"isInterface":false,"name":"webdriver.Capabilities","isTypedef":false,"href":"class_webdriver_Capabilities.html"},{"isInterface":false,"name":"webdriver.Capability","isTypedef":false,"href":"enum_webdriver_Capability.html"},{"isInterface":false,"name":"webdriver.Command","isTypedef":false,"href":"class_webdriver_Command.html"},{"isInterface":true,"name":"webdriver.CommandExecutor","isTypedef":false,"href":"interface_webdriver_CommandExecutor.html"},{"isInterface":false,"name":"webdriver.CommandName","isTypedef":false,"href":"enum_webdriver_CommandName.html"},{"isInterface":false,"name":"webdriver.EventEmitter","isTypedef":false,"href":"class_webdriver_EventEmitter.html"},{"isInterface":false,"name":"webdriver.FirefoxDomExecutor","isTypedef":false,"href":"class_webdriver_FirefoxDomExecutor.html"},{"isInterface":false,"name":"webdriver.FirefoxDomExecutor.Attribute_","isTypedef":false,"href":"enum_webdriver_FirefoxDomExecutor_Attribute_.html"},{"isInterface":false,"name":"webdriver.FirefoxDomExecutor.EventType_","isTypedef":false,"href":"enum_webdriver_FirefoxDomExecutor_EventType_.html"},{"isInterface":false,"name":"webdriver.Key","isTypedef":false,"href":"enum_webdriver_Key.html"},{"isInterface":false,"name":"webdriver.Locator","isTypedef":false,"href":"class_webdriver_Locator.html"},{"isInterface":false,"name":"webdriver.Session","isTypedef":false,"href":"class_webdriver_Session.html"},{"isInterface":false,"name":"webdriver.UnhandledAlertError","isTypedef":false,"href":"class_webdriver_UnhandledAlertError.html"},{"isInterface":false,"name":"webdriver.WebDriver","isTypedef":false,"href":"class_webdriver_WebDriver.html"},{"isInterface":false,"name":"webdriver.WebDriver.Logs","isTypedef":false,"href":"class_webdriver_WebDriver_Logs.html"},{"isInterface":false,"name":"webdriver.WebDriver.Navigation","isTypedef":false,"href":"class_webdriver_WebDriver_Navigation.html"},{"isInterface":false,"name":"webdriver.WebDriver.Options","isTypedef":false,"href":"class_webdriver_WebDriver_Options.html"},{"isInterface":false,"name":"webdriver.WebDriver.TargetLocator","isTypedef":false,"href":"class_webdriver_WebDriver_TargetLocator.html"},{"isInterface":false,"name":"webdriver.WebDriver.Timeouts","isTypedef":false,"href":"class_webdriver_WebDriver_Timeouts.html"},{"isInterface":false,"name":"webdriver.WebDriver.Window","isTypedef":false,"href":"class_webdriver_WebDriver_Window.html"},{"isInterface":false,"name":"webdriver.WebElement","isTypedef":false,"href":"class_webdriver_WebElement.html"},{"isInterface":false,"name":"webdriver.http","isTypedef":false,"href":"namespace_webdriver_http.html"},{"isInterface":true,"name":"webdriver.http.Client","isTypedef":false,"href":"interface_webdriver_http_Client.html"},{"isInterface":false,"name":"webdriver.http.CorsClient","isTypedef":false,"href":"class_webdriver_http_CorsClient.html"},{"isInterface":false,"name":"webdriver.http.Executor","isTypedef":false,"href":"class_webdriver_http_Executor.html"},{"isInterface":false,"name":"webdriver.http.Request","isTypedef":false,"href":"class_webdriver_http_Request.html"},{"isInterface":false,"name":"webdriver.http.Response","isTypedef":false,"href":"class_webdriver_http_Response.html"},{"isInterface":false,"name":"webdriver.http.XhrClient","isTypedef":false,"href":"class_webdriver_http_XhrClient.html"},{"isInterface":false,"name":"webdriver.logging","isTypedef":false,"href":"namespace_webdriver_logging.html"},{"name":"webdriver.logging.Preferences","isTypedef":true,"href":"namespace_webdriver_logging.html#webdriver.logging.Preferences"},{"isInterface":false,"name":"webdriver.logging.Entry","isTypedef":false,"href":"class_webdriver_logging_Entry.html"},{"isInterface":false,"name":"webdriver.logging.Level","isTypedef":false,"href":"enum_webdriver_logging_Level.html"},{"isInterface":false,"name":"webdriver.logging.LevelName","isTypedef":false,"href":"enum_webdriver_logging_LevelName.html"},{"isInterface":false,"name":"webdriver.logging.Type","isTypedef":false,"href":"enum_webdriver_logging_Type.html"},{"isInterface":false,"name":"webdriver.process","isTypedef":false,"href":"namespace_webdriver_process.html"},{"isInterface":false,"name":"webdriver.promise","isTypedef":false,"href":"namespace_webdriver_promise.html"},{"isInterface":false,"name":"webdriver.promise.CanceledTaskError_","isTypedef":false,"href":"class_webdriver_promise_CanceledTaskError_.html"},{"isInterface":false,"name":"webdriver.promise.ControlFlow","isTypedef":false,"href":"class_webdriver_promise_ControlFlow.html"},{"name":"webdriver.promise.ControlFlow.Timer","isTypedef":true,"href":"class_webdriver_promise_ControlFlow.html#webdriver.promise.ControlFlow.Timer"},{"isInterface":false,"name":"webdriver.promise.ControlFlow.EventType","isTypedef":false,"href":"enum_webdriver_promise_ControlFlow_EventType.html"},{"isInterface":false,"name":"webdriver.promise.Deferred","isTypedef":false,"href":"class_webdriver_promise_Deferred.html"},{"name":"webdriver.promise.Deferred.Listener_","isTypedef":true,"href":"class_webdriver_promise_Deferred.html#webdriver.promise.Deferred.Listener_"},{"isInterface":false,"name":"webdriver.promise.Deferred.State_","isTypedef":false,"href":"enum_webdriver_promise_Deferred_State_.html"},{"isInterface":false,"name":"webdriver.promise.Frame_","isTypedef":false,"href":"class_webdriver_promise_Frame_.html"},{"isInterface":false,"name":"webdriver.promise.Node_","isTypedef":false,"href":"class_webdriver_promise_Node_.html"},{"isInterface":false,"name":"webdriver.promise.Promise","isTypedef":false,"href":"class_webdriver_promise_Promise.html"},{"isInterface":false,"name":"webdriver.promise.Task_","isTypedef":false,"href":"class_webdriver_promise_Task_.html"},{"isInterface":false,"name":"webdriver.stacktrace","isTypedef":false,"href":"namespace_webdriver_stacktrace.html"},{"isInterface":false,"name":"webdriver.stacktrace.Frame","isTypedef":false,"href":"class_webdriver_stacktrace_Frame.html"},{"isInterface":false,"name":"webdriver.stacktrace.Snapshot","isTypedef":false,"href":"class_webdriver_stacktrace_Snapshot.html"},{"isInterface":false,"name":"webdriver.testing","isTypedef":false,"href":"namespace_webdriver_testing.html"},{"isInterface":false,"name":"webdriver.testing.Assertion","isTypedef":false,"href":"class_webdriver_testing_Assertion.html"},{"isInterface":false,"name":"webdriver.testing.Assertion.DelegatingMatcher_","isTypedef":false,"href":"class_webdriver_testing_Assertion_DelegatingMatcher_.html"},{"isInterface":false,"name":"webdriver.testing.ContainsMatcher","isTypedef":false,"href":"class_webdriver_testing_ContainsMatcher.html"},{"isInterface":false,"name":"webdriver.testing.NegatedAssertion","isTypedef":false,"href":"class_webdriver_testing_NegatedAssertion.html"},{"isInterface":false,"name":"webdriver.testing.assert","isTypedef":false,"href":"module_selenium-webdriver_testing_assert_namespace_dossier$$module__$usr$local$google$home$jleyba$dev$selenium$build$javascript$node$selenium_webdriver$testing$assert_exports.html"},{"isInterface":false,"name":"webdriver.testing.asserts","isTypedef":false,"href":"namespace_webdriver_testing_asserts.html"}],"modules":[{"name":"selenium-webdriver","types":[{"isInterface":false,"name":"Command","isTypedef":false,"href":"class_webdriver_Command.html"},{"isInterface":false,"name":"Key","isTypedef":false,"href":"enum_webdriver_Key.html"},{"isInterface":false,"name":"EventEmitter","isTypedef":false,"href":"class_webdriver_EventEmitter.html"},{"isInterface":false,"name":"Browser","isTypedef":false,"href":"enum_webdriver_Browser.html"},{"isInterface":false,"name":"ActionSequence","isTypedef":false,"href":"class_webdriver_ActionSequence.html"},{"isInterface":false,"name":"Button","isTypedef":false,"href":"enum_webdriver_Button.html"},{"isInterface":false,"name":"CommandName","isTypedef":false,"href":"enum_webdriver_CommandName.html"},{"isInterface":false,"name":"WebElement","isTypedef":false,"href":"class_webdriver_WebElement.html"},{"isInterface":false,"name":"Capability","isTypedef":false,"href":"enum_webdriver_Capability.html"},{"isInterface":false,"name":"Session","isTypedef":false,"href":"class_webdriver_Session.html"},{"isInterface":false,"name":"Builder","isTypedef":false,"href":"module_selenium-webdriver_class_Builder.html"},{"isInterface":false,"name":"Capabilities","isTypedef":false,"href":"class_webdriver_Capabilities.html"},{"isInterface":false,"name":"WebDriver","isTypedef":false,"href":"class_webdriver_WebDriver.html"}],"href":"module_selenium-webdriver.html"},{"name":"selenium-webdriver/_base","types":[],"href":"module_selenium-webdriver__base.html"},{"name":"selenium-webdriver/builder","types":[{"isInterface":false,"name":"Builder","isTypedef":false,"href":"module_selenium-webdriver_builder_class_Builder.html"}],"href":"module_selenium-webdriver_builder.html"},{"name":"selenium-webdriver/chrome","types":[{"isInterface":false,"name":"ServiceBuilder","isTypedef":false,"href":"module_selenium-webdriver_chrome_class_ServiceBuilder.html"},{"isInterface":false,"name":"Options","isTypedef":false,"href":"module_selenium-webdriver_chrome_class_Options.html"}],"href":"module_selenium-webdriver_chrome.html"},{"name":"selenium-webdriver/error","types":[{"isInterface":false,"name":"ErrorCode","isTypedef":false,"href":"enum_bot_ErrorCode.html"},{"isInterface":false,"name":"Error","isTypedef":false,"href":"class_bot_Error.html"}],"href":"module_selenium-webdriver_error.html"},{"name":"selenium-webdriver/executors","types":[],"href":"module_selenium-webdriver_executors.html"},{"name":"selenium-webdriver/http","types":[{"isInterface":false,"name":"HttpClient","isTypedef":false,"href":"module_selenium-webdriver_http_class_HttpClient.html"},{"isInterface":false,"name":"Response","isTypedef":false,"href":"class_webdriver_http_Response.html"},{"isInterface":false,"name":"Request","isTypedef":false,"href":"class_webdriver_http_Request.html"},{"isInterface":false,"name":"Executor","isTypedef":false,"href":"class_webdriver_http_Executor.html"}],"href":"module_selenium-webdriver_http.html"},{"name":"selenium-webdriver/http/util","types":[],"href":"module_selenium-webdriver_http_util.html"},{"name":"selenium-webdriver/io","types":[],"href":"module_selenium-webdriver_io.html"},{"name":"selenium-webdriver/net","types":[],"href":"module_selenium-webdriver_net.html"},{"name":"selenium-webdriver/net/portprober","types":[],"href":"module_selenium-webdriver_net_portprober.html"},{"name":"selenium-webdriver/phantomjs","types":[],"href":"module_selenium-webdriver_phantomjs.html"},{"name":"selenium-webdriver/proxy","types":[{"isInterface":false,"name":"ProxyConfig","isTypedef":true,"href":"module_selenium-webdriver_proxy_namespace_ProxyConfig.html"}],"href":"module_selenium-webdriver_proxy.html"},{"name":"selenium-webdriver/remote","types":[{"isInterface":false,"name":"DriverService","isTypedef":false,"href":"module_selenium-webdriver_remote_class_DriverService.html"},{"isInterface":false,"name":"SeleniumServer","isTypedef":false,"href":"module_selenium-webdriver_remote_class_SeleniumServer.html"},{"name":"SeleniumServer.Options","isTypedef":true,"href":""},{"isInterface":false,"name":"ServiceOptions","isTypedef":true,"href":"module_selenium-webdriver_remote_namespace_ServiceOptions.html"}],"href":"module_selenium-webdriver_remote.html"},{"name":"selenium-webdriver/testing","types":[],"href":"module_selenium-webdriver_testing.html"},{"name":"selenium-webdriver/testing/assert","types":[],"href":"module_selenium-webdriver_testing_assert.html"}]}; \ No newline at end of file +var TYPES = {"types":[{"name":"bot","href":"namespace_bot.html","namespace":true,"interface":false},{"name":"bot.Error","href":"class_bot_Error.html","namespace":false,"interface":false,"members":["code","isAutomationError","state","toString"]},{"name":"bot.Error.State","href":"enum_bot_Error_State.html","namespace":false,"interface":false},{"name":"bot.ErrorCode","href":"enum_bot_ErrorCode.html","namespace":false,"interface":false},{"name":"bot.json","href":"namespace_bot_json.html","namespace":true,"interface":false,"statics":["NATIVE_JSON","parse","stringify"]},{"name":"bot.response","href":"namespace_bot_response.html","namespace":true,"interface":false,"statics":["checkResponse","createErrorResponse","createResponse","isResponseObject"]},{"name":"bot.response.ResponseObject","href":"namespace_bot_response.html#response.ResponseObject"},{"name":"bot.userAgent","href":"namespace_bot_userAgent.html","namespace":true,"interface":false,"statics":["ANDROID_PRE_GINGERBREAD","ANDROID_PRE_ICECREAMSANDWICH","FIREFOX_EXTENSION","IE_DOC_10","IE_DOC_9","IE_DOC_PRE10","IE_DOC_PRE8","IE_DOC_PRE9","IOS","MOBILE","SAFARI_6","WINDOWS_PHONE","isEngineVersion","isProductVersion"]},{"name":"webdriver","href":"namespace_webdriver.html","namespace":true,"interface":false},{"name":"webdriver.ProxyConfig","href":"namespace_webdriver.html#webdriver.ProxyConfig"},{"name":"webdriver.ActionSequence","href":"class_webdriver_ActionSequence.html","namespace":false,"interface":false,"members":["click","doubleClick","dragAndDrop","keyDown","keyUp","mouseDown","mouseMove","mouseUp","perform","sendKeys"]},{"name":"webdriver.Alert","href":"class_webdriver_Alert.html","namespace":false,"interface":false,"members":["accept","dismiss","getText","sendKeys"]},{"name":"webdriver.AlertPromise","href":"class_webdriver_AlertPromise.html","namespace":false,"interface":false,"members":["accept","cancel","dismiss","getText","isPending","sendKeys","then","thenCatch","thenFinally"]},{"name":"webdriver.Browser","href":"enum_webdriver_Browser.html","namespace":false,"interface":false},{"name":"webdriver.Button","href":"enum_webdriver_Button.html","namespace":false,"interface":false},{"name":"webdriver.By","href":"namespace_webdriver_By.html","namespace":true,"interface":false,"statics":["className","css","id","js","linkText","name","partialLinkText","tagName","xpath"]},{"name":"webdriver.By.Hash","href":"namespace_webdriver_By.html#By.Hash"},{"name":"webdriver.Capabilities","href":"class_webdriver_Capabilities.html","namespace":false,"interface":false,"statics":["Capabilities.android","Capabilities.chrome","Capabilities.firefox","Capabilities.htmlunit","Capabilities.htmlunitwithjs","Capabilities.ie","Capabilities.ipad","Capabilities.iphone","Capabilities.opera","Capabilities.phantomjs","Capabilities.safari"],"members":["get","has","merge","serialize","set","setAlertBehavior","setEnableNativeEvents","setLoggingPrefs","setProxy","setScrollBehavior"]},{"name":"webdriver.Capability","href":"enum_webdriver_Capability.html","namespace":false,"interface":false},{"name":"webdriver.Command","href":"class_webdriver_Command.html","namespace":false,"interface":false,"members":["getName","getParameter","getParameters","setParameter","setParameters"]},{"name":"webdriver.CommandExecutor","href":"interface_webdriver_CommandExecutor.html","namespace":true,"interface":true,"members":["execute"]},{"name":"webdriver.CommandName","href":"enum_webdriver_CommandName.html","namespace":false,"interface":false},{"name":"webdriver.EventEmitter","href":"class_webdriver_EventEmitter.html","namespace":false,"interface":false,"members":["addListener","emit","listeners","on","once","removeAllListeners","removeListener"]},{"name":"webdriver.FileDetector","href":"class_webdriver_FileDetector.html","namespace":false,"interface":false,"members":["handleFile"]},{"name":"webdriver.Key","href":"enum_webdriver_Key.html","namespace":false,"interface":false,"statics":["Key.chord"]},{"name":"webdriver.Locator","href":"class_webdriver_Locator.html","namespace":false,"interface":false,"statics":["Locator.Strategy","Locator.checkLocator"],"members":["using","value","toString"]},{"name":"webdriver.Serializable","href":"class_webdriver_Serializable.html","namespace":false,"interface":false,"members":["serialize"]},{"name":"webdriver.Session","href":"class_webdriver_Session.html","namespace":false,"interface":false,"members":["getCapabilities","getCapability","getId","toJSON"]},{"name":"webdriver.TouchSequence","href":"class_webdriver_TouchSequence.html","namespace":false,"interface":false,"members":["doubleTap","flick","flickElement","longPress","move","perform","release","scroll","scrollFromElement","tap","tapAndHold"]},{"name":"webdriver.UnhandledAlertError","href":"class_webdriver_UnhandledAlertError.html","namespace":false,"interface":false,"members":["getAlertText"]},{"name":"webdriver.WebDriver","href":"class_webdriver_WebDriver.html","namespace":false,"interface":false,"statics":["WebDriver.attachToSession","WebDriver.createSession"],"members":["actions","call","close","controlFlow","executeAsyncScript","executeScript","findElement","findElements","get","getAllWindowHandles","getCapabilities","getCurrentUrl","getPageSource","getSession","getTitle","getWindowHandle","isElementPresent","manage","navigate","quit","schedule","setFileDetector","sleep","switchTo","takeScreenshot","touchActions","wait"]},{"name":"webdriver.WebDriver.Logs","href":"class_webdriver_WebDriver_Logs.html","namespace":false,"interface":false,"members":["get","getAvailableLogTypes"]},{"name":"webdriver.WebDriver.Navigation","href":"class_webdriver_WebDriver_Navigation.html","namespace":false,"interface":false,"members":["back","forward","refresh","to"]},{"name":"webdriver.WebDriver.Options","href":"class_webdriver_WebDriver_Options.html","namespace":false,"interface":false,"members":["addCookie","deleteAllCookies","deleteCookie","getCookie","getCookies","logs","timeouts","window"]},{"name":"webdriver.WebDriver.Options.Cookie","href":"class_webdriver_WebDriver_Options.html#Options.Cookie"},{"name":"webdriver.WebDriver.TargetLocator","href":"class_webdriver_WebDriver_TargetLocator.html","namespace":false,"interface":false,"members":["activeElement","alert","defaultContent","frame","window"]},{"name":"webdriver.WebDriver.Timeouts","href":"class_webdriver_WebDriver_Timeouts.html","namespace":false,"interface":false,"members":["implicitlyWait","pageLoadTimeout","setScriptTimeout"]},{"name":"webdriver.WebDriver.Window","href":"class_webdriver_WebDriver_Window.html","namespace":false,"interface":false,"members":["getPosition","getSize","maximize","setPosition","setSize"]},{"name":"webdriver.WebElement","href":"class_webdriver_WebElement.html","namespace":false,"interface":false,"statics":["WebElement.ELEMENT_KEY","WebElement.equals"],"members":["clear","click","findElement","findElements","getAttribute","getCssValue","getDriver","getId","getInnerHtml","getLocation","getOuterHtml","getRawId","getSize","getTagName","getText","isDisplayed","isElementPresent","isEnabled","isSelected","sendKeys","serialize","submit"]},{"name":"webdriver.WebElement.Id","href":"class_webdriver_WebElement.html#WebElement.Id"},{"name":"webdriver.WebElementPromise","href":"class_webdriver_WebElementPromise.html","namespace":false,"interface":false,"members":["cancel","getId","isPending","then","thenCatch","thenFinally"]},{"name":"webdriver.http","href":"namespace_webdriver_http.html","namespace":true,"interface":false},{"name":"webdriver.http.Client","href":"interface_webdriver_http_Client.html","namespace":true,"interface":true,"members":["send"]},{"name":"webdriver.http.Executor","href":"class_webdriver_http_Executor.html","namespace":false,"interface":false,"members":["defineCommand","execute"]},{"name":"webdriver.http.Request","href":"class_webdriver_http_Request.html","namespace":false,"interface":false,"members":["data","headers","method","path","toString"]},{"name":"webdriver.http.Response","href":"class_webdriver_http_Response.html","namespace":false,"interface":false,"statics":["Response.fromXmlHttpRequest"],"members":["body","headers","status","toString"]},{"name":"webdriver.logging","href":"namespace_webdriver_logging.html","namespace":true,"interface":false,"statics":["addConsoleHandler","getLevel","getLogger","installConsoleHandler","removeConsoleHandler"]},{"name":"webdriver.logging.Entry","href":"class_webdriver_logging_Entry.html","namespace":false,"interface":false,"statics":["Entry.fromClosureLogRecord"],"members":["level","message","timestamp","type","toJSON"]},{"name":"webdriver.logging.Level","href":"class_webdriver_logging_Level.html","namespace":false,"interface":false,"statics":["Level.ALL","Level.CONFIG","Level.DEBUG","Level.FINE","Level.FINER","Level.FINEST","Level.INFO","Level.OFF","Level.PREDEFINED_LEVELS","Level.SEVERE","Level.SHOUT","Level.WARNING","Level.getPredefinedLevel","Level.getPredefinedLevelByValue"],"members":["name","value","toString"]},{"name":"webdriver.logging.LogRecord","href":"class_webdriver_logging_LogRecord.html","namespace":false,"interface":false,"statics":["LogRecord.ENABLE_SEQUENCE_NUMBERS"],"members":["getException","getLevel","getLoggerName","getMessage","getMillis","getSequenceNumber","reset","setException","setLevel","setLoggerName","setMessage","setMillis"]},{"name":"webdriver.logging.Logger","href":"class_webdriver_logging_Logger.html","namespace":false,"interface":false,"statics":["Logger.ENABLE_HIERARCHY","Logger.ROOT_LOGGER_NAME","Logger.getLogger","Logger.logToProfilers"],"members":["addHandler","config","fine","finer","finest","getChildren","getEffectiveLevel","getLevel","getLogRecord","getName","getParent","info","isLoggable","log","logRecord","removeHandler","setLevel","severe","shout","warning"]},{"name":"webdriver.logging.Preferences","href":"class_webdriver_logging_Preferences.html","namespace":false,"interface":false,"members":["setLevel","toJSON"]},{"name":"webdriver.logging.Type","href":"enum_webdriver_logging_Type.html","namespace":false,"interface":false},{"name":"webdriver.promise","href":"namespace_webdriver_promise.html","namespace":true,"interface":false,"statics":["LONG_STACK_TRACES","all","asap","captureStackTrace","checkedNodeCall","consume","controlFlow","createFlow","defer","delayed","filter","fulfilled","fullyResolved","isGenerator","isPromise","map","rejected","setDefaultFlow","when"]},{"name":"webdriver.promise.CancellationError","href":"class_webdriver_promise_CancellationError.html","namespace":false,"interface":false,"statics":["CancellationError.wrap"]},{"name":"webdriver.promise.ControlFlow","href":"class_webdriver_promise_ControlFlow.html","namespace":false,"interface":false,"members":["async","execute","getSchedule","isIdle","reset","setPropagateUnhandledRejections","timeout","toString","wait"]},{"name":"webdriver.promise.ControlFlow.EventType","href":"enum_webdriver_promise_ControlFlow_EventType.html","namespace":false,"interface":false},{"name":"webdriver.promise.Deferred","href":"class_webdriver_promise_Deferred.html","namespace":false,"interface":false,"members":["promise","cancel","fulfill","isPending","reject","then","thenCatch","thenFinally"]},{"name":"webdriver.promise.MultipleUnhandledRejectionError","href":"class_webdriver_promise_MultipleUnhandledRejectionError.html","namespace":false,"interface":false,"members":["errors"]},{"name":"webdriver.promise.Promise","href":"class_webdriver_promise_Promise.html","namespace":false,"interface":false,"members":["cancel","isPending","then","thenCatch","thenFinally","toString"]},{"name":"webdriver.promise.Thenable","href":"interface_webdriver_promise_Thenable.html","namespace":true,"interface":true,"statics":["Thenable.addImplementation","Thenable.isImplementation"],"members":["cancel","isPending","then","thenCatch","thenFinally"]},{"name":"webdriver.stacktrace","href":"namespace_webdriver_stacktrace.html","namespace":true,"interface":false,"statics":["BROWSER_SUPPORTED","format","get","getStack"]},{"name":"webdriver.stacktrace.Frame","href":"class_webdriver_stacktrace_Frame.html","namespace":false,"interface":false,"members":["column","getColumn","getLine","getName","getUrl","isAnonymous","toString"]},{"name":"webdriver.stacktrace.Snapshot","href":"class_webdriver_stacktrace_Snapshot.html","namespace":false,"interface":false,"members":["getStacktrace"]},{"name":"webdriver.testing","href":"namespace_webdriver_testing.html","namespace":true,"interface":false},{"name":"webdriver.testing.Assertion","href":"class_webdriver_testing_Assertion.html","namespace":false,"interface":false,"members":["is","not","apply","closeTo","contains","endsWith","equalTo","greaterThan","greaterThanEqualTo","instanceOf","isFalse","isNull","isNullOrUndefined","isTrue","isUndefined","lessThan","lessThanEqualTo","matches","startsWith"]},{"name":"webdriver.testing.Assertion.DelegatingMatcher_","href":"class_webdriver_testing_Assertion_DelegatingMatcher_.html","namespace":false,"interface":false,"members":["describe","matches"]},{"name":"webdriver.testing.ContainsMatcher","href":"class_webdriver_testing_ContainsMatcher.html","namespace":false,"interface":false,"members":["describe","matches"]},{"name":"webdriver.testing.NegatedAssertion","href":"class_webdriver_testing_NegatedAssertion.html","namespace":false,"interface":false,"members":["value","apply"]},{"name":"webdriver.testing.assert","href":"namespace_webdriver_testing_assert.html","namespace":true,"interface":false,"statics":["register"]},{"name":"webdriver.testing.asserts","href":"namespace_webdriver_testing_asserts.html","namespace":true,"interface":false,"statics":["assertThat","equalTo"]},{"name":"webdriver.until","href":"namespace_webdriver_until.html","namespace":true,"interface":false,"statics":["ableToSwitchToFrame","alertIsPresent","elementIsDisabled","elementIsEnabled","elementIsNotSelected","elementIsNotVisible","elementIsSelected","elementIsVisible","elementLocated","elementTextContains","elementTextIs","elementTextMatches","elementsLocated","stalenessOf","titleContains","titleIs","titleMatches"]},{"name":"webdriver.until.Condition","href":"class_webdriver_until_Condition.html","namespace":false,"interface":false,"members":["description","fn"]}],"modules":[{"name":"selenium-webdriver","href":"module_selenium-webdriver.html","types":[{"name":"WebDriver","href":"module_selenium-webdriver_class_WebDriver.html","namespace":false,"interface":false},{"name":"Serializable","href":"module_selenium-webdriver_class_Serializable.html","namespace":false,"interface":false},{"name":"Capability","href":"module_selenium-webdriver_enum_Capability.html","namespace":false,"interface":false},{"name":"ActionSequence","href":"module_selenium-webdriver_class_ActionSequence.html","namespace":false,"interface":false},{"name":"Builder","href":"module_selenium-webdriver_class_Builder.html","namespace":false,"interface":false},{"name":"promise","href":"module_selenium-webdriver_namespace_promise.html","namespace":true,"interface":false},{"name":"WebElement","href":"module_selenium-webdriver_class_WebElement.html","namespace":false,"interface":false},{"name":"error","href":"module_selenium-webdriver_namespace_error.html","namespace":true,"interface":false},{"name":"WebElementPromise","href":"module_selenium-webdriver_class_WebElementPromise.html","namespace":false,"interface":false},{"name":"FileDetector","href":"module_selenium-webdriver_class_FileDetector.html","namespace":false,"interface":false},{"name":"stacktrace","href":"module_selenium-webdriver_namespace_stacktrace.html","namespace":true,"interface":false},{"name":"Button","href":"module_selenium-webdriver_enum_Button.html","namespace":false,"interface":false},{"name":"Command","href":"module_selenium-webdriver_class_Command.html","namespace":false,"interface":false},{"name":"EventEmitter","href":"module_selenium-webdriver_class_EventEmitter.html","namespace":false,"interface":false},{"name":"Capabilities","href":"module_selenium-webdriver_class_Capabilities.html","namespace":false,"interface":false},{"name":"By","href":"module_selenium-webdriver_namespace_By.html","namespace":true,"interface":false},{"name":"logging","href":"module_selenium-webdriver_namespace_logging.html","namespace":true,"interface":false},{"name":"until","href":"module_selenium-webdriver_namespace_until.html","namespace":true,"interface":false},{"name":"CommandName","href":"module_selenium-webdriver_enum_CommandName.html","namespace":false,"interface":false},{"name":"Key","href":"module_selenium-webdriver_enum_Key.html","namespace":false,"interface":false},{"name":"Browser","href":"module_selenium-webdriver_enum_Browser.html","namespace":false,"interface":false},{"name":"Session","href":"module_selenium-webdriver_class_Session.html","namespace":false,"interface":false}]},{"name":"selenium-webdriver/_base","href":"module_selenium-webdriver__base.html","statics":["closure","exportPublicApi","isDevMode","require"],"types":[{"name":"Context","href":"module_selenium-webdriver__base_class_Context.html","namespace":false,"interface":false,"members":["closure"]}]},{"name":"selenium-webdriver/builder","href":"module_selenium-webdriver_builder.html","types":[{"name":"Builder","href":"module_selenium-webdriver_builder_class_Builder.html","namespace":false,"interface":false,"members":["build","disableEnvironmentOverrides","forBrowser","getCapabilities","getServerUrl","getWebDriverProxy","setAlertBehavior","setChromeOptions","setControlFlow","setEnableNativeEvents","setFirefoxOptions","setIeOptions","setLoggingPrefs","setOperaOptions","setProxy","setSafariOptions","setScrollBehavior","usingServer","usingWebDriverProxy","withCapabilities"]}]},{"name":"selenium-webdriver/chrome","href":"module_selenium-webdriver_chrome.html","statics":["getDefaultService","setDefaultService"],"types":[{"name":"Options","href":"module_selenium-webdriver_chrome_class_Options.html","namespace":false,"interface":false,"statics":["Options.fromCapabilities"],"members":["addArguments","addExtensions","androidActivity","androidChrome","androidDeviceSerial","androidPackage","androidProcess","androidUseRunningApp","detachDriver","excludeSwitches","serialize","setChromeBinaryPath","setChromeLogFile","setChromeMinidumpPath","setLocalState","setLoggingPrefs","setMobileEmulation","setPerfLoggingPrefs","setProxy","setUserPreferences","toCapabilities"]},{"name":"Driver","href":"module_selenium-webdriver_chrome_class_Driver.html","namespace":false,"interface":false,"members":["launchApp","setFileDetector"]},{"name":"ServiceBuilder","href":"module_selenium-webdriver_chrome_class_ServiceBuilder.html","namespace":false,"interface":false,"members":["build","enableVerboseLogging","loggingTo","setAdbPort","setNumHttpThreads","setStdio","setUrlBasePath","usingPort","withEnvironment"]}]},{"name":"selenium-webdriver/error","href":"module_selenium-webdriver_error.html","types":[{"name":"Error","href":"module_selenium-webdriver_error_class_Error.html","namespace":false,"interface":false},{"name":"ErrorCode","href":"module_selenium-webdriver_error_enum_ErrorCode.html","namespace":false,"interface":false}]},{"name":"selenium-webdriver/executors","href":"module_selenium-webdriver_executors.html","statics":["createExecutor"],"types":[{"name":"DeferredExecutor","href":"module_selenium-webdriver_executors_class_DeferredExecutor.html","namespace":false,"interface":false,"members":["execute"]}]},{"name":"selenium-webdriver/firefox","href":"module_selenium-webdriver_firefox.html","types":[{"name":"Options","href":"module_selenium-webdriver_firefox_class_Options.html","namespace":false,"interface":false,"members":["setBinary","setLoggingPreferences","setProfile","setProxy","toCapabilities"]},{"name":"Driver","href":"module_selenium-webdriver_firefox_class_Driver.html","namespace":false,"interface":false,"members":["quit","setFileDetector"]},{"name":"Binary","href":"module_selenium-webdriver_firefox_class_Binary.html","namespace":false,"interface":false},{"name":"Profile","href":"module_selenium-webdriver_firefox_class_Profile.html","namespace":false,"interface":false}]},{"name":"selenium-webdriver/firefox/binary","href":"module_selenium-webdriver_firefox_binary.html","types":[{"name":"Binary","href":"module_selenium-webdriver_firefox_binary_class_Binary.html","namespace":false,"interface":false,"members":["addArguments","launch","serialize"]}]},{"name":"selenium-webdriver/firefox/extension","href":"module_selenium-webdriver_firefox_extension.html","statics":["install"]},{"name":"selenium-webdriver/firefox/profile","href":"module_selenium-webdriver_firefox_profile.html","statics":["decode","loadUserPrefs"],"types":[{"name":"Profile","href":"module_selenium-webdriver_firefox_profile_class_Profile.html","namespace":false,"interface":false,"members":["acceptUntrustedCerts","addExtension","assumeUntrustedCertIssuer","encode","getPort","getPreference","nativeEventsEnabled","serialize","setAcceptUntrustedCerts","setAssumeUntrustedCertIssuer","setNativeEventsEnabled","setPort","setPreference","writeToDisk"]}]},{"name":"selenium-webdriver/http","href":"module_selenium-webdriver_http.html","types":[{"name":"Response","href":"module_selenium-webdriver_http_class_Response.html","namespace":false,"interface":false},{"name":"Executor","href":"module_selenium-webdriver_http_class_Executor.html","namespace":false,"interface":false},{"name":"HttpClient","href":"module_selenium-webdriver_http_class_HttpClient.html","namespace":false,"interface":false,"members":["send"]},{"name":"Request","href":"module_selenium-webdriver_http_class_Request.html","namespace":false,"interface":false}]},{"name":"selenium-webdriver/http/util","href":"module_selenium-webdriver_http_util.html","statics":["getStatus","waitForServer","waitForUrl"]},{"name":"selenium-webdriver/ie","href":"module_selenium-webdriver_ie.html","types":[{"name":"Options","href":"module_selenium-webdriver_ie_class_Options.html","namespace":false,"interface":false,"statics":["Options.fromCapabilities"],"members":["addArguments","browserAttachTimeout","enableElementCacheCleanup","enablePersistentHover","ensureCleanSession","forceCreateProcessApi","ignoreZoomSetting","initialBrowserUrl","introduceFlakinessByIgnoringProtectedModeSettings","requireWindowFocus","setExtractPath","setHost","setLogFile","setLogLevel","setProxy","silent","toCapabilities","usePerProcessProxy"]},{"name":"Driver","href":"module_selenium-webdriver_ie_class_Driver.html","namespace":false,"interface":false,"members":["setFileDetector"]},{"name":"Level","href":"module_selenium-webdriver_ie_enum_Level.html","namespace":false,"interface":false}]},{"name":"selenium-webdriver/io","href":"module_selenium-webdriver_io.html","statics":["copy","copyDir","exists","findInPath","rmDir","tmpDir","tmpFile","unlink"]},{"name":"selenium-webdriver/io/exec","href":"module_selenium-webdriver_io_exec.html"},{"name":"selenium-webdriver/net","href":"module_selenium-webdriver_net.html","statics":["getAddress","getLoopbackAddress"]},{"name":"selenium-webdriver/net/portprober","href":"module_selenium-webdriver_net_portprober.html","statics":["findFreePort","isFree"]},{"name":"selenium-webdriver/opera","href":"module_selenium-webdriver_opera.html","statics":["getDefaultService","setDefaultService"],"types":[{"name":"Options","href":"module_selenium-webdriver_opera_class_Options.html","namespace":false,"interface":false,"statics":["Options.fromCapabilities"],"members":["addArguments","addExtensions","serialize","setLoggingPrefs","setOperaBinaryPath","setProxy","toCapabilities"]},{"name":"Driver","href":"module_selenium-webdriver_opera_class_Driver.html","namespace":false,"interface":false,"members":["setFileDetector"]},{"name":"ServiceBuilder","href":"module_selenium-webdriver_opera_class_ServiceBuilder.html","namespace":false,"interface":false,"members":["build","enableVerboseLogging","loggingTo","setStdio","silent","usingPort","withEnvironment"]}]},{"name":"selenium-webdriver/phantomjs","href":"module_selenium-webdriver_phantomjs.html","types":[{"name":"Driver","href":"module_selenium-webdriver_phantomjs_class_Driver.html","namespace":false,"interface":false,"members":["executePhantomJS","setFileDetector"]}]},{"name":"selenium-webdriver/proxy","href":"module_selenium-webdriver_proxy.html","statics":["direct","manual","pac","system"]},{"name":"selenium-webdriver/remote","href":"module_selenium-webdriver_remote.html","types":[{"name":"FileDetector","href":"module_selenium-webdriver_remote_class_FileDetector.html","namespace":false,"interface":false,"members":["handleFile"]},{"name":"DriverService","href":"module_selenium-webdriver_remote_class_DriverService.html","namespace":false,"interface":false,"statics":["DriverService.DEFAULT_START_TIMEOUT_MS"],"members":["address","isRunning","kill","start","stop"]},{"name":"SeleniumServer","href":"module_selenium-webdriver_remote_class_SeleniumServer.html","namespace":false,"interface":false},{"name":"SeleniumServer.Options","href":"module_selenium-webdriver_remote_class_SeleniumServer.html#SeleniumServer.Options"}]},{"name":"selenium-webdriver/safari","href":"module_selenium-webdriver_safari.html","types":[{"name":"Options","href":"module_selenium-webdriver_safari_class_Options.html","namespace":false,"interface":false,"statics":["Options.fromCapabilities"],"members":["serialize","setCleanSession","setLoggingPrefs","toCapabilities"]},{"name":"Driver","href":"module_selenium-webdriver_safari_class_Driver.html","namespace":false,"interface":false}]},{"name":"selenium-webdriver/testing","href":"module_selenium-webdriver_testing.html","statics":["after","afterEach","before","beforeEach","describe","ignore","iit","it","xdescribe","xit"]},{"name":"selenium-webdriver/testing/assert","href":"module_selenium-webdriver_testing_assert.html","statics":["register"]}]}; \ No newline at end of file diff --git a/error.js b/error.js index 368bbc2..6e1fdd7 100644 --- a/error.js +++ b/error.js @@ -1,17 +1,19 @@ -// Copyright 2014 Selenium committers -// Copyright 2014 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; diff --git a/example/chrome_android.js b/example/chrome_android.js new file mode 100644 index 0000000..990a4c4 --- /dev/null +++ b/example/chrome_android.js @@ -0,0 +1,38 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * @fileoverview A basic example of working with Chrome on Android. Before + * running this example, you must start adb and connect a device (or start an + * AVD). + */ + +var webdriver = require('..'), + By = webdriver.By, + until = webdriver.until, + chrome = require('../chrome'); + +var driver = new webdriver.Builder() + .forBrowser('chrome') + .setChromeOptions(new chrome.Options().androidChrome()) + .build(); + +driver.get('http://www.google.com/ncr'); +driver.findElement(By.name('q')).sendKeys('webdriver'); +driver.findElement(By.name('btnG')).click(); +driver.wait(until.titleIs('webdriver - Google Search'), 1000); +driver.quit(); diff --git a/example/chrome_mobile_emulation.js b/example/chrome_mobile_emulation.js new file mode 100644 index 0000000..d308112 --- /dev/null +++ b/example/chrome_mobile_emulation.js @@ -0,0 +1,39 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 This is an example of emulating a mobile device using the + * ChromeDriver. + */ + +var webdriver = require('..'), + By = webdriver.By, + until = webdriver.until, + chrome = require('../chrome'); + + +var driver = new webdriver.Builder() + .forBrowser('chrome') + .setChromeOptions(new chrome.Options() + .setMobileEmulation({deviceName: 'Google Nexus 5'})) + .build(); + +driver.get('http://www.google.com/ncr'); +driver.findElement(By.name('q')).sendKeys('webdriver'); +driver.findElement(By.name('btnG')).click(); +driver.wait(until.titleIs('webdriver - Google Search'), 1000); +driver.quit(); diff --git a/example/google_search.js b/example/google_search.js index d09e41d..c624fa2 100644 --- a/example/google_search.js +++ b/example/google_search.js @@ -1,40 +1,50 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. /** * @fileoverview An example WebDriver script. This requires the chromedriver * to be present on the system PATH. - * Usage: node selenium-webdriver/example/google_search.js + * + * Usage: + * // Default behavior + * node selenium-webdriver/example/google_search.js + * + * // Target Chrome locally; the chromedriver must be on your PATH + * SELENIUM_BROWSER=chrome node selenium-webdriver/example/google_search.js + * + * // Use a local copy of the standalone Selenium server + * SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \ + * node selenium-webdriver/example/google_search.js + * + * // Target a remove Selenium server + * SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \ + * node selenium-webdriver/example/google_search.js */ -var fs = require('fs'); - var webdriver = require('..'), - remote = require('../remote'); - -var driver = new webdriver.Builder(). - withCapabilities(webdriver.Capabilities.chrome()). - build(); + By = webdriver.By, + until = webdriver.until; -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); +var driver = new webdriver.Builder() + .forBrowser('firefox') + .build(); -driver.quit(); +driver.get('http://www.google.com/ncr'); +driver.findElement(By.name('q')).sendKeys('webdriver'); +driver.findElement(By.name('btnG')).click(); +driver.wait(until.titleIs('webdriver - Google Search'), 1000); +driver.quit(); \ No newline at end of file diff --git a/example/google_search_generator.js b/example/google_search_generator.js new file mode 100644 index 0000000..90173bc --- /dev/null +++ b/example/google_search_generator.js @@ -0,0 +1,47 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * @fileoverview An example WebDriver script using Harmony generator functions. + * This requires node v0.11 or newer. + * + * Usage: node --harmony-generators \ + * selenium-webdriver/example/google_search_generator.js + */ + +var webdriver = require('..'), + By = webdriver.By; + +var driver = new webdriver.Builder() + .forBrowser('firefox') + .build(); + +driver.get('http://www.google.com/ncr'); +driver.call(function* () { + var query = yield driver.findElement(By.name('q')); + query.sendKeys('webdriver'); + + var submit = yield driver.findElement(By.name('btnG')); + submit.click(); +}); + +driver.wait(function* () { + var title = yield driver.getTitle(); + return 'webdriver - Google Search' === title; +}, 1000); + +driver.quit(); diff --git a/example/google_search_test.js b/example/google_search_test.js index 8ed6a7d..823e2c5 100644 --- a/example/google_search_test.js +++ b/example/google_search_test.js @@ -1,50 +1,47 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. /** - * @fileoverview An example test that may be run using Mocha. To run, you must - * have the chromedriver installed on the system PATH. + * @fileoverview An example test that may be run using Mocha. + * Usage: mocha -t 10000 selenium-webdriver/example/google_search_test.js */ -var assert = require('assert'), - fs = require('fs'); - var webdriver = require('..'), - test = require('../testing'), - remote = require('../remote'); - + By = webdriver.By, + until = webdriver.until, + test = require('../testing'); test.describe('Google Search', function() { var driver; test.before(function() { - driver = new webdriver.Builder(). - withCapabilities(webdriver.Capabilities.chrome()). - build(); + driver = new webdriver.Builder() + .forBrowser('firefox') + .build(); }); 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); + driver.findElement(By.name('q')).sendKeys('webdriver'); + driver.findElement(By.name('btnG')).click(); + driver.wait(until.titleIs('webdriver - Google Search'), 1000); }); - test.after(function() { driver.quit(); }); + test.after(function() { + driver.quit(); + }); }); diff --git a/example/logging.js b/example/logging.js new file mode 100644 index 0000000..ad198f1 --- /dev/null +++ b/example/logging.js @@ -0,0 +1,39 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Demonstrates how to use WebDriver's logging sysem. + */ + +'use strict'; + +var webdriver = require('..'), + By = webdriver.By, + until = webdriver.until; + +webdriver.logging.installConsoleHandler(); +webdriver.logging.getLogger().setLevel(webdriver.logging.Level.ALL); + +var driver = new webdriver.Builder() + .forBrowser('firefox') + .build(); + +driver.get('http://www.google.com/ncr'); +driver.findElement(By.name('q')).sendKeys('webdriver'); +driver.findElement(By.name('btnG')).click(); +driver.wait(until.titleIs('webdriver - Google Search'), 1000); +driver.quit(); diff --git a/example/parallel_flows.js b/example/parallel_flows.js new file mode 100644 index 0000000..f416922 --- /dev/null +++ b/example/parallel_flows.js @@ -0,0 +1,51 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * @fileoverview An example of starting multiple WebDriver clients that run + * in parallel in separate control flows. + */ + +var webdriver = require('..'), + By = webdriver.By, + until = webdriver.until; + +for (var i = 0; i < 3; i++) { + (function(n) { + var flow = new webdriver.promise.ControlFlow() + .on('uncaughtException', function(e) { + console.log('uncaughtException in flow %d: %s', n, e); + }); + + var driver = new webdriver.Builder(). + forBrowser('firefox'). + setControlFlow(flow). // Comment out this line to see the difference. + build(); + + // Position and resize window so it's easy to see them running together. + driver.manage().window().setSize(600, 400); + driver.manage().window().setPosition(300 * i, 400 * i); + + driver.get('http://www.google.com'); + driver.findElement(By.name('q')).sendKeys('webdriver'); + driver.findElement(By.name('btnG')).click(); + driver.wait(until.titleIs('webdriver - Google Search'), 1000); + + driver.quit(); + })(i); +} + diff --git a/executors.js b/executors.js index 79cf8c1..481040d 100644 --- a/executors.js +++ b/executors.js @@ -1,16 +1,19 @@ -// Copyright 2013 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Various utilities for working with @@ -45,15 +48,19 @@ var DeferredExecutor = function(delegate) { // PUBLIC API +exports.DeferredExecutor = DeferredExecutor; + /** * Creates a command executor that uses WebDriver's JSON wire protocol. * @param {(string|!webdriver.promise.Promise.)} url The server's URL, * or a promise that will resolve to that URL. + * @param {string=} opt_proxy (optional) The URL of the HTTP proxy for the + * client to use. * @returns {!webdriver.CommandExecutor} The new command executor. */ -exports.createExecutor = function(url) { +exports.createExecutor = function(url, opt_proxy) { return new DeferredExecutor(promise.when(url, function(url) { - var client = new HttpClient(url); + var client = new HttpClient(url, null, opt_proxy); return new HttpExecutor(client); })); }; diff --git a/firefox/binary.js b/firefox/binary.js new file mode 100644 index 0000000..259805c --- /dev/null +++ b/firefox/binary.js @@ -0,0 +1,229 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Manages Firefox binaries. This module is considered internal; + * users should use {@link selenium-webdriver/firefox}. + */ + +'use strict'; + +var child = require('child_process'), + fs = require('fs'), + path = require('path'), + util = require('util'); + +var Serializable = require('..').Serializable, + promise = require('..').promise, + _base = require('../_base'), + io = require('../io'), + exec = require('../io/exec'); + + + +/** @const */ +var NO_FOCUS_LIB_X86 = _base.isDevMode() ? + path.join(__dirname, '../../../../cpp/prebuilt/i386/libnoblur.so') : + path.join(__dirname, '../lib/firefox/i386/libnoblur.so') ; + +/** @const */ +var NO_FOCUS_LIB_AMD64 = _base.isDevMode() ? + path.join(__dirname, '../../../../cpp/prebuilt/amd64/libnoblur64.so') : + path.join(__dirname, '../lib/firefox/amd64/libnoblur64.so') ; + +var X_IGNORE_NO_FOCUS_LIB = 'x_ignore_nofocus.so'; + +var foundBinary = null; + + +/** + * Checks the default Windows Firefox locations in Program Files. + * @return {!promise.Promise.} A promise for the located executable. + * The promise will resolve to {@code null} if Fireox was not found. + */ +function defaultWindowsLocation() { + var files = [ + process.env['PROGRAMFILES'] || 'C:\\Program Files', + process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)' + ].map(function(prefix) { + return path.join(prefix, 'Mozilla Firefox\\firefox.exe'); + }); + return io.exists(files[0]).then(function(exists) { + return exists ? files[0] : io.exists(files[1]).then(function(exists) { + return exists ? files[1] : null; + }); + }); +} + + +/** + * Locates the Firefox binary for the current system. + * @return {!promise.Promise.} A promise for the located binary. The + * promise will be rejected if Firefox cannot be located. + */ +function findFirefox() { + if (foundBinary) { + return foundBinary; + } + + if (process.platform === 'darwin') { + var osxExe = '/Applications/Firefox.app/Contents/MacOS/firefox-bin'; + foundBinary = io.exists(osxExe).then(function(exists) { + return exists ? osxExe : null; + }); + } else if (process.platform === 'win32') { + foundBinary = defaultWindowsLocation(); + } else { + foundBinary = promise.fulfilled(io.findInPath('firefox')); + } + + return foundBinary = foundBinary.then(function(found) { + if (found) { + return found; + } + throw Error('Could not locate Firefox on the current system'); + }); +} + + +/** + * Copies the no focus libs into the given profile directory. + * @param {string} profileDir Path to the profile directory to install into. + * @return {!promise.Promise.} The LD_LIBRARY_PATH prefix string to use + * for the installed libs. + */ +function installNoFocusLibs(profileDir) { + var x86 = path.join(profileDir, 'x86'); + var amd64 = path.join(profileDir, 'amd64'); + + return mkdir(x86) + .then(copyLib.bind(null, NO_FOCUS_LIB_X86, x86)) + .then(mkdir.bind(null, amd64)) + .then(copyLib.bind(null, NO_FOCUS_LIB_AMD64, amd64)) + .then(function() { + return x86 + ':' + amd64; + }); + + function mkdir(dir) { + return io.exists(dir).then(function(exists) { + if (!exists) { + return promise.checkedNodeCall(fs.mkdir, dir); + } + }); + } + + function copyLib(src, dir) { + return io.copy(src, path.join(dir, X_IGNORE_NO_FOCUS_LIB)); + } +} + + +/** + * Provides a mechanism to configure and launch Firefox in a subprocess for + * use with WebDriver. + * + * @param {string=} opt_exe Path to the Firefox binary to use. If not + * specified, will attempt to locate Firefox on the current system. + * @constructor + * @extends {Serializable.} + */ +var Binary = function(opt_exe) { + Serializable.call(this); + + /** @private {(string|undefined)} */ + this.exe_ = opt_exe; + + /** @private {!Array.} */ + this.args_ = []; + + /** @private {!Object.} */ + this.env_ = {}; + Object.keys(process.env).forEach(function(key) { + this.env_[key] = process.env[key]; + }.bind(this)); + this.env_['MOZ_CRASHREPORTER_DISABLE'] = '1'; + this.env_['MOZ_NO_REMOTE'] = '1'; + this.env_['NO_EM_RESTART'] = '1'; +}; +util.inherits(Binary, Serializable); + + +/** + * Add arguments to the command line used to start Firefox. + * @param {...(string|!Array.)} var_args Either the arguments to add as + * varargs, or the arguments as an array. + */ +Binary.prototype.addArguments = function(var_args) { + for (var i = 0; i < arguments.length; i++) { + if (util.isArray(arguments[i])) { + this.args_ = this.args_.concat(arguments[i]); + } else { + this.args_.push(arguments[i]); + } + } +}; + + +/** + * Launches Firefox and returns a promise that will be fulfilled when the + * process terminates. + * @param {string} profile Path to the profile directory to use. + * @return {!promise.Promise.} A promise for the handle to the + * started subprocess. + */ +Binary.prototype.launch = function(profile) { + var env = {}; + Object.keys(this.env_).forEach(function(key) { + env[key] = this.env_[key]; + }.bind(this)); + env['XRE_PROFILE_PATH'] = profile; + + var args = ['-foreground'].concat(this.args_); + + return promise.when(this.exe_ || findFirefox(), function(firefox) { + if (process.platform === 'win32' || process.platform === 'darwin') { + return exec(firefox, {args: args, env: env}); + } + return installNoFocusLibs(profile).then(function(ldLibraryPath) { + env['LD_LIBRARY_PATH'] = ldLibraryPath + ':' + env['LD_LIBRARY_PATH']; + env['LD_PRELOAD'] = X_IGNORE_NO_FOCUS_LIB; + return exec(firefox, {args: args, env: env}); + }); + }); +}; + + +/** + * Returns a promise for the wire representation of this binary. Note: the + * FirefoxDriver only supports passing the path to the binary executable over + * the wire; all command line arguments and environment variables will be + * discarded. + * + * @return {!promise.Promise.} A promise for this binary's wire + * representation. + * @override + */ +Binary.prototype.serialize = function() { + return promise.when(this.exe_ || findFirefox()); +}; + + +// PUBLIC API + + +exports.Binary = Binary; + diff --git a/firefox/extension.js b/firefox/extension.js new file mode 100644 index 0000000..1ba6966 --- /dev/null +++ b/firefox/extension.js @@ -0,0 +1,185 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Firefox extensions. */ + +'use strict'; + +var AdmZip = require('adm-zip'), + fs = require('fs'), + path = require('path'), + util = require('util'), + xml = require('xml2js'); + +var promise = require('..').promise, + checkedCall = promise.checkedNodeCall, + io = require('../io'); + + +/** + * Thrown when there an add-on is malformed. + * @param {string} msg The error message. + * @constructor + * @extends {Error} + */ +function AddonFormatError(msg) { + Error.call(this); + + Error.captureStackTrace(this, AddonFormatError); + + /** @override */ + this.name = AddonFormatError.name; + + /** @override */ + this.message = msg; +} +util.inherits(AddonFormatError, Error); + + + +/** + * Installs an extension to the given directory. + * @param {string} extension Path to the extension to install, as either a xpi + * file or a directory. + * @param {string} dir Path to the directory to install the extension in. + * @return {!promise.Promise.} A promise for the add-on ID once + * installed. + */ +function install(extension, dir) { + return getDetails(extension).then(function(details) { + function returnId() { return details.id; } + + var dst = path.join(dir, details.id); + if (extension.slice(-4) === '.xpi') { + if (!details.unpack) { + return io.copy(extension, dst + '.xpi').then(returnId); + } else { + return checkedCall(fs.readFile, extension).then(function(buff) { + // TODO: find an async library for inflating a zip archive. + new AdmZip(buff).extractAllTo(dst, true); + }).then(returnId); + } + } else { + return io.copyDir(extension, dst).then(returnId); + } + }); +} + + +/** + * Describes a Firefox add-on. + * @typedef {{id: string, name: string, version: string, unpack: boolean}} + */ +var AddonDetails; + + +/** + * Extracts the details needed to install an add-on. + * @param {string} addonPath Path to the extension directory. + * @return {!promise.Promise.} A promise for the add-on details. + */ +function getDetails(addonPath) { + return readManifest(addonPath).then(function(doc) { + var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#'); + var rdf = getNamespaceId( + doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + + var description = doc[rdf + 'RDF'][rdf + 'Description'][0]; + var details = { + id: getNodeText(description, em + 'id'), + name: getNodeText(description, em + 'name'), + version: getNodeText(description, em + 'version'), + unpack: getNodeText(description, em + 'unpack') || false + }; + + if (typeof details.unpack === 'string') { + details.unpack = details.unpack.toLowerCase() === 'true'; + } + + if (!details.id) { + throw new AddonFormatError('Could not find add-on ID for ' + addonPath); + } + + return details; + }); + + function getNodeText(node, name) { + return node[name] && node[name][0] || ''; + } + + function getNamespaceId(doc, url) { + var keys = Object.keys(doc); + if (keys.length !== 1) { + throw new AddonFormatError('Malformed manifest for add-on ' + addonPath); + } + + var namespaces = doc[keys[0]].$; + var id = ''; + Object.keys(namespaces).some(function(ns) { + if (namespaces[ns] !== url) { + return false; + } + + if (ns.indexOf(':') != -1) { + id = ns.split(':')[1] + ':'; + } + return true; + }); + return id; + } +} + + +/** + * Reads the manifest for a Firefox add-on. + * @param {string} addonPath Path to a Firefox add-on as a xpi or an extension. + * @return {!promise.Promise.} A promise for the parsed manifest. + */ +function readManifest(addonPath) { + var manifest; + + if (addonPath.slice(-4) === '.xpi') { + manifest = checkedCall(fs.readFile, addonPath).then(function(buff) { + var zip = new AdmZip(buff); + if (!zip.getEntry('install.rdf')) { + throw new AddonFormatError( + 'Could not find install.rdf in ' + addonPath); + } + var done = promise.defer(); + zip.readAsTextAsync('install.rdf', done.fulfill); + return done.promise; + }); + } else { + manifest = checkedCall(fs.stat, addonPath).then(function(stats) { + if (!stats.isDirectory()) { + throw Error( + 'Add-on path is niether a xpi nor a directory: ' + addonPath); + } + return checkedCall(fs.readFile, path.join(addonPath, 'install.rdf')); + }); + } + + return manifest.then(function(content) { + return checkedCall(xml.parseString, content); + }); +} + + +// PUBLIC API + + +exports.install = install; diff --git a/firefox/index.js b/firefox/index.js new file mode 100644 index 0000000..c0eef2d --- /dev/null +++ b/firefox/index.js @@ -0,0 +1,320 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Defines the {@linkplain Driver WebDriver} client for Firefox. + * Each FirefoxDriver instance will be created with an anonymous profile, + * ensuring browser historys do not share session data (cookies, history, cache, + * offline storage, etc.) + * + * __Customizing the Firefox Profile__ + * + * The {@link Profile} class may be used to configure the browser profile used + * with WebDriver, with functions to install additional + * {@linkplain Profile#addExtension extensions}, configure browser + * {@linkplain Profile#setPreference preferences}, and more. For example, you + * may wish to include Firebug: + * + * var firefox = require('selenium-webdriver/firefox'); + * + * var profile = new firefox.Profile(); + * profile.addExtension('/path/to/firebug.xpi'); + * profile.setPreference('extensions.firebug.showChromeErrors', true); + * + * var options = new firefox.Options().setProfile(profile); + * var driver = new firefox.Driver(options); + * + * The {@link Profile} class may also be used to configure WebDriver based on a + * pre-existing browser profile: + * + * var profile = new firefox.Profile( + * '/usr/local/home/bob/.mozilla/firefox/3fgog75h.testing'); + * var options = new firefox.Options().setProfile(profile); + * var driver = new firefox.Driver(options); + * + * The FirefoxDriver will _never_ modify a pre-existing profile; instead it will + * create a copy for it to modify. By extension, there are certain browser + * preferences that are required for WebDriver to function properly and they + * will always be overwritten. + * + * __Using a Custom Firefox Binary__ + * + * On Windows and OSX, the FirefoxDriver will search for Firefox in its + * default installation location: + * + * * Windows: C:\Program Files and C:\Program Files (x86). + * * Mac OS X: /Applications/Firefox.app + * + * For Linux, Firefox will be located on the PATH: `$(where firefox)`. + * + * You can configure WebDriver to start use a custom Firefox installation with + * the {@link Binary} class: + * + * var firefox = require('selenium-webdriver/firefox'); + * var binary = new firefox.Binary('/my/firefox/install/dir/firefox-bin'); + * var options = new firefox.Options().setBinary(binary); + * var driver = new firefox.Driver(options); + * + * __Remote Testing__ + * + * You may customize the Firefox binary and profile when running against a + * remote Selenium server. Your custom profile will be packaged as a zip and + * transfered to the remote host for use. The profile will be transferred + * _once for each new session_. The performance impact should be minimal if + * you've only configured a few extra browser preferences. If you have a large + * profile with several extensions, you should consider installing it on the + * remote host and defining its path via the {@link Options} class. Custom + * binaries are never copied to remote machines and must be referenced by + * installation path. + * + * var options = new firefox.Options() + * .setProfile('/profile/path/on/remote/host') + * .setBinary('/install/dir/on/remote/host/firefox-bin'); + * + * var driver = new (require('selenium-webdriver')).Builder() + * .forBrowser('firefox') + * .usingServer('http://127.0.0.1:4444/wd/hub') + * .setFirefoxOptions(options) + * .build(); + */ + +'use strict'; + +var url = require('url'), + util = require('util'); + +var Binary = require('./binary').Binary, + Profile = require('./profile').Profile, + decodeProfile = require('./profile').decode, + webdriver = require('..'), + executors = require('../executors'), + httpUtil = require('../http/util'), + io = require('../io'), + net = require('../net'), + portprober = require('../net/portprober'); + + +/** + * Configuration options for the FirefoxDriver. + * @constructor + */ +var Options = function() { + /** @private {Profile} */ + this.profile_ = null; + + /** @private {Binary} */ + this.binary_ = null; + + /** @private {webdriver.logging.Preferences} */ + this.logPrefs_ = null; + + /** @private {webdriver.ProxyConfig} */ + this.proxy_ = null; +}; + + +/** + * Sets the profile to use. The profile may be specified as a + * {@link Profile} object or as the path to an existing Firefox profile to use + * as a template. + * + * @param {(string|!Profile)} profile The profile to use. + * @return {!Options} A self reference. + */ +Options.prototype.setProfile = function(profile) { + if (typeof profile === 'string') { + profile = new Profile(profile); + } + this.profile_ = profile; + return this; +}; + + +/** + * Sets the binary to use. The binary may be specified as the path to a Firefox + * executable, or as a {@link Binary} object. + * + * @param {(string|!Binary)} binary The binary to use. + * @return {!Options} A self reference. + */ +Options.prototype.setBinary = function(binary) { + if (typeof binary === 'string') { + binary = new Binary(binary); + } + this.binary_ = binary; + return this; +}; + + +/** + * Sets the logging preferences for the new session. + * @param {webdriver.logging.Preferences} prefs The logging preferences. + * @return {!Options} A self reference. + */ +Options.prototype.setLoggingPreferences = function(prefs) { + this.logPrefs_ = prefs; + return this; +}; + + +/** + * Sets the proxy to use. + * + * @param {webdriver.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ +Options.prototype.setProxy = function(proxy) { + this.proxy_ = proxy; + return this; +}; + + +/** + * Converts these options to a {@link webdriver.Capabilities} instance. + * + * @return {!webdriver.Capabilities} A new capabilities object. + */ +Options.prototype.toCapabilities = function(opt_remote) { + var caps = webdriver.Capabilities.firefox(); + if (this.logPrefs_) { + caps.set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_); + } + if (this.proxy_) { + caps.set(webdriver.Capability.PROXY, this.proxy_); + } + if (this.binary_) { + caps.set('firefox_binary', this.binary_); + } + if (this.profile_) { + caps.set('firefox_profile', this.profile_); + } + return caps; +}; + + +/** + * A WebDriver client for Firefox. + * + * @param {(Options|webdriver.Capabilities|Object)=} opt_config The + * configuration options for this driver, specified as either an + * {@link Options} or {@link webdriver.Capabilities}, or as a raw hash + * object. + * @param {webdriver.promise.ControlFlow=} opt_flow The flow to + * schedule commands through. Defaults to the active flow object. + * @constructor + * @extends {webdriver.WebDriver} + */ +var Driver = function(opt_config, opt_flow) { + var caps; + if (opt_config instanceof Options) { + caps = opt_config.toCapabilities(); + } else { + caps = new webdriver.Capabilities(opt_config); + } + + var binary = caps.get('firefox_binary') || new Binary(); + if (typeof binary === 'string') { + binary = new Binary(binary); + } + + var profile = caps.get('firefox_profile') || new Profile(); + + caps.set('firefox_binary', null); + caps.set('firefox_profile', null); + + /** @private {?string} */ + this.profilePath_ = null; + + var self = this; + var freePort = portprober.findFreePort(); + + /** @private */ + this.command_ = freePort.then(function(port) { + if (typeof profile === 'string') { + return decodeProfile(profile).then(function(dir) { + var profile = new Profile(dir); + profile.setPreference('webdriver_firefox_port', port); + return profile.writeToDisk(); + }); + } else { + profile.setPreference('webdriver_firefox_port', port); + return profile.writeToDisk(); + } + }).then(function(profileDir) { + self.profilePath_ = profileDir; + return binary.launch(profileDir); + }); + + var serverUrl = this.command_ + .then(function() { return freePort; }) + .then(function(port) { + var serverUrl = url.format({ + protocol: 'http', + hostname: net.getLoopbackAddress(), + port: port, + pathname: '/hub' + }); + + return httpUtil.waitForServer(serverUrl, 45 * 1000).then(function() { + return serverUrl; + }); + }); + + var executor = executors.createExecutor(serverUrl); + var driver = webdriver.WebDriver.createSession(executor, caps, opt_flow); + + webdriver.WebDriver.call(this, driver.getSession(), executor, opt_flow); +}; +util.inherits(Driver, webdriver.WebDriver); + + +/** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ +Driver.prototype.setFileDetector = function() { +}; + + +/** @override */ +Driver.prototype.quit = function() { + return this.call(function() { + var self = this; + return Driver.super_.prototype.quit.call(this) + .thenFinally(function() { + return self.command_.then(function(command) { + command.kill(); + return command.result(); + }); + }) + .thenFinally(function() { + if (self.profilePath_) { + return io.rmDir(self.profilePath_); + } + }); + }, this); +}; + + +// PUBLIC API + + +exports.Binary = Binary; +exports.Driver = Driver; +exports.Options = Options; +exports.Profile = Profile; diff --git a/firefox/profile.js b/firefox/profile.js new file mode 100644 index 0000000..e198f93 --- /dev/null +++ b/firefox/profile.js @@ -0,0 +1,433 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Profile management module. This module is considered internal; + * users should use {@link selenium-webdriver/firefox}. + */ + +'use strict'; + +var AdmZip = require('adm-zip'), + AdmConstants = require('adm-zip/util/constants'), + fs = require('fs'), + path = require('path'), + util = require('util'), + vm = require('vm'); + +var Serializable = require('..').Serializable, + promise = require('..').promise, + _base = require('../_base'), + io = require('../io'), + extension = require('./extension'); + + +/** @const */ +var WEBDRIVER_PREFERENCES_PATH = _base.isDevMode() + ? path.join(__dirname, '../../../firefox-driver/webdriver.json') + : path.join(__dirname, '../lib/firefox/webdriver.json'); + +/** @const */ +var WEBDRIVER_EXTENSION_PATH = _base.isDevMode() + ? path.join(__dirname, + '../../../../build/javascript/firefox-driver/webdriver.xpi') + : path.join(__dirname, '../lib/firefox/webdriver.xpi'); + +/** @const */ +var WEBDRIVER_EXTENSION_NAME = 'fxdriver@googlecode.com'; + + + +/** @type {Object} */ +var defaultPreferences = null; + +/** + * Synchronously loads the default preferences used for the FirefoxDriver. + * @return {!Object} The default preferences JSON object. + */ +function getDefaultPreferences() { + if (!defaultPreferences) { + var contents = fs.readFileSync(WEBDRIVER_PREFERENCES_PATH, 'utf8'); + defaultPreferences = JSON.parse(contents); + } + return defaultPreferences; +} + + +/** + * Parses a user.js file in a Firefox profile directory. + * @param {string} f Path to the file to parse. + * @return {!promise.Promise.} A promise for the parsed preferences as + * a JSON object. If the file does not exist, an empty object will be + * returned. + */ +function loadUserPrefs(f) { + var done = promise.defer(); + fs.readFile(f, function(err, contents) { + if (err && err.code === 'ENOENT') { + done.fulfill({}); + return; + } + + if (err) { + done.reject(err); + return; + } + + var prefs = {}; + var context = vm.createContext({ + 'user_pref': function(key, value) { + prefs[key] = value; + } + }); + + vm.runInContext(contents, context, f); + done.fulfill(prefs); + }); + return done.promise; +} + + +/** + * Copies the properties of one object into another. + * @param {!Object} a The destination object. + * @param {!Object} b The source object to apply as a mixin. + */ +function mixin(a, b) { + Object.keys(b).forEach(function(key) { + a[key] = b[key]; + }); +} + + +/** + * @param {!Object} defaults The default preferences to write. Will be + * overridden by user.js preferences in the template directory and the + * frozen preferences required by WebDriver. + * @param {string} dir Path to the directory write the file to. + * @return {!promise.Promise.} A promise for the profile directory, + * to be fulfilled when user preferences have been written. + */ +function writeUserPrefs(prefs, dir) { + var userPrefs = path.join(dir, 'user.js'); + return loadUserPrefs(userPrefs).then(function(overrides) { + mixin(prefs, overrides); + mixin(prefs, getDefaultPreferences()['frozen']); + + var contents = Object.keys(prefs).map(function(key) { + return 'user_pref(' + JSON.stringify(key) + ', ' + + JSON.stringify(prefs[key]) + ');'; + }).join('\n'); + + var done = promise.defer(); + fs.writeFile(userPrefs, contents, function(err) { + err && done.reject(err) || done.fulfill(dir); + }); + return done.promise; + }); +}; + + +/** + * Installs a group of extensions in the given profile directory. If the + * WebDriver extension is not included in this set, the default version + * bundled with this package will be installed. + * @param {!Array.} extensions The extensions to install, as a + * path to an unpacked extension directory or a path to a xpi file. + * @param {string} dir The profile directory to install to. + * @param {boolean=} opt_excludeWebDriverExt Whether to skip installation of + * the default WebDriver extension. + * @return {!promise.Promise.} A promise for the main profile directory + * once all extensions have been installed. + */ +function installExtensions(extensions, dir, opt_excludeWebDriverExt) { + var hasWebDriver = !!opt_excludeWebDriverExt; + var next = 0; + var extensionDir = path.join(dir, 'extensions'); + var done = promise.defer(); + + return io.exists(extensionDir).then(function(exists) { + if (!exists) { + return promise.checkedNodeCall(fs.mkdir, extensionDir); + } + }).then(function() { + installNext(); + return done.promise; + }); + + function installNext() { + if (!done.isPending()) { + return; + } + + if (next >= extensions.length) { + if (hasWebDriver) { + done.fulfill(dir); + } else { + install(WEBDRIVER_EXTENSION_PATH); + } + } else { + install(extensions[next++]); + } + } + + function install(ext) { + extension.install(ext, extensionDir).then(function(id) { + hasWebDriver = hasWebDriver || (id === WEBDRIVER_EXTENSION_NAME); + installNext(); + }, done.reject); + } +} + + +/** + * Decodes a base64 encoded profile. + * @param {string} data The base64 encoded string. + * @return {!promise.Promise.} A promise for the path to the decoded + * profile directory. + */ +function decode(data) { + return io.tmpFile().then(function(file) { + var buf = new Buffer(data, 'base64'); + return promise.checkedNodeCall(fs.writeFile, file, buf).then(function() { + return io.tmpDir(); + }).then(function(dir) { + var zip = new AdmZip(file); + zip.extractAllTo(dir); // Sync only? Why?? :-( + return dir; + }); + }); +} + + + +/** + * Models a Firefox proifle directory for use with the FirefoxDriver. The + * {@code Proifle} directory uses an in-memory model until {@link #writeToDisk} + * is called. + * @param {string=} opt_dir Path to an existing Firefox profile directory to + * use a template for this profile. If not specified, a blank profile will + * be used. + * @constructor + * @extends {Serializable.} + */ +var Profile = function(opt_dir) { + Serializable.call(this); + + /** @private {!Object} */ + this.preferences_ = {}; + + mixin(this.preferences_, getDefaultPreferences()['mutable']); + mixin(this.preferences_, getDefaultPreferences()['frozen']); + + /** @private {boolean} */ + this.nativeEventsEnabled_ = true; + + /** @private {(string|undefined)} */ + this.template_ = opt_dir; + + /** @private {number} */ + this.port_ = 0; + + /** @private {!Array.} */ + this.extensions_ = []; +}; +util.inherits(Profile, Serializable); + + +/** + * Registers an extension to be included with this profile. + * @param {string} extension Path to the extension to include, as either an + * unpacked extension directory or the path to a xpi file. + */ +Profile.prototype.addExtension = function(extension) { + this.extensions_.push(extension); +}; + + +/** + * Sets a desired preference for this profile. + * @param {string} key The preference key. + * @param {(string|number|boolean)} value The preference value. + * @throws {Error} If attempting to set a frozen preference. + */ +Profile.prototype.setPreference = function(key, value) { + var frozen = getDefaultPreferences()['frozen']; + if (frozen.hasOwnProperty(key) && frozen[key] !== value) { + throw Error('You may not set ' + key + '=' + JSON.stringify(value) + + '; value is frozen for proper WebDriver functionality (' + + key + '=' + JSON.stringify(frozen[key]) + ')'); + } + this.preferences_[key] = value; +}; + + +/** + * Returns the currently configured value of a profile preference. This does + * not include any defaults defined in the profile's template directory user.js + * file (if a template were specified on construction). + * @param {string} key The desired preference. + * @return {(string|number|boolean|undefined)} The current value of the + * requested preference. + */ +Profile.prototype.getPreference = function(key) { + return this.preferences_[key]; +}; + + +/** + * @return {number} The port this profile is currently configured to use, or + * 0 if the port will be selected at random when the profile is written + * to disk. + */ +Profile.prototype.getPort = function() { + return this.port_; +}; + + +/** + * Sets the port to use for the WebDriver extension loaded by this profile. + * @param {number} port The desired port, or 0 to use any free port. + */ +Profile.prototype.setPort = function(port) { + this.port_ = port; +}; + + +/** + * @return {boolean} Whether the FirefoxDriver is configured to automatically + * accept untrusted SSL certificates. + */ +Profile.prototype.acceptUntrustedCerts = function() { + return !!this.preferences_['webdriver_accept_untrusted_certs']; +}; + + +/** + * Sets whether the FirefoxDriver should automatically accept untrusted SSL + * certificates. + * @param {boolean} value . + */ +Profile.prototype.setAcceptUntrustedCerts = function(value) { + this.preferences_['webdriver_accept_untrusted_certs'] = !!value; +}; + + +/** + * Sets whether to assume untrusted certificates come from untrusted issuers. + * @param {boolean} value . + */ +Profile.prototype.setAssumeUntrustedCertIssuer = function(value) { + this.preferences_['webdriver_assume_untrusted_issuer'] = !!value; +}; + + +/** + * @return {boolean} Whether to assume untrusted certs come from untrusted + * issuers. + */ +Profile.prototype.assumeUntrustedCertIssuer = function() { + return !!this.preferences_['webdriver_assume_untrusted_issuer']; +}; + + +/** + * Sets whether to use native events with this profile. + * @param {boolean} enabled . + */ +Profile.prototype.setNativeEventsEnabled = function(enabled) { + this.nativeEventsEnabled_ = enabled; +}; + + +/** + * Returns whether native events are enabled in this profile. + * @return {boolean} . + */ +Profile.prototype.nativeEventsEnabled = function() { + return this.nativeEventsEnabled_; +}; + + +/** + * Writes this profile to disk. + * @param {boolean=} opt_excludeWebDriverExt Whether to exclude the WebDriver + * extension from the generated profile. Used to reduce the size of an + * {@link #encode() encoded profile} since the server will always install + * the extension itself. + * @return {!promise.Promise.} A promise for the path to the new + * profile directory. + */ +Profile.prototype.writeToDisk = function(opt_excludeWebDriverExt) { + var profileDir = io.tmpDir(); + if (this.template_) { + profileDir = profileDir.then(function(dir) { + return io.copyDir( + this.template_, dir, /(parent\.lock|lock|\.parentlock)/); + }.bind(this)); + } + + // Freeze preferences for async operations. + var prefs = {}; + mixin(prefs, this.preferences_); + + // Freeze extensions for async operations. + var extensions = this.extensions_.concat(); + + return profileDir.then(function(dir) { + return writeUserPrefs(prefs, dir); + }).then(function(dir) { + return installExtensions(extensions, dir, !!opt_excludeWebDriverExt); + }); +}; + + +/** + * Encodes this profile as a zipped, base64 encoded directory. + * @return {!promise.Promise.} A promise for the encoded profile. + */ +Profile.prototype.encode = function() { + return this.writeToDisk(true).then(function(dir) { + var zip = new AdmZip(); + zip.addLocalFolder(dir, ''); + zip.getEntries()[0].header.method = AdmConstants.STORED; + return io.tmpFile().then(function(file) { + zip.writeZip(file); // Sync! Why oh why :-( + return promise.checkedNodeCall(fs.readFile, file); + }); + }).then(function(data) { + return new Buffer(data).toString('base64'); + }); +}; + + +/** + * Encodes this profile as a zipped, base64 encoded directory. + * @return {!promise.Promise.} A promise for the encoded profile. + * @override + */ +Profile.prototype.serialize = function() { + return this.encode(); +}; + + +// PUBLIC API + + +exports.Profile = Profile; +exports.decode = decode; +exports.loadUserPrefs = loadUserPrefs; diff --git a/http/index.js b/http/index.js index cb4f7a1..8f06a8c 100644 --- a/http/index.js +++ b/http/index.js @@ -1,19 +1,22 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Defines a the {@code webdriver.http.Client} for use with + * @fileoverview Defines the {@code webdriver.http.Client} for use with * NodeJS. */ @@ -26,25 +29,35 @@ var base = require('../_base'), var KeepAliveAgent = require('keep-alive-agent'), agent = new KeepAliveAgent(); - /** * A {@link webdriver.http.Client} implementation using Node's built-in http * module. * @param {string} serverUrl URL for the WebDriver server to send commands to. + * @param {http.Agent=} opt_agent The agent to use for each request. + * Defaults to {@code http.globalAgent}. + * @param {string=} opt_proxy The proxy to use for the connection to the server. + * Default is to use no proxy. * @constructor * @implements {webdriver.http.Client} */ -var HttpClient = function(serverUrl) { +var HttpClient = function(serverUrl, opt_agent, opt_proxy) { var parsedUrl = url.parse(serverUrl); if (!parsedUrl.hostname) { throw new Error('Invalid server URL: ' + serverUrl); } + /** @private {http.Agent} */ + this.agent_ = opt_agent || agent; + + /** @private {string} */ + this.proxy_ = opt_proxy; + /** * Base options for each request. * @private {!Object} */ this.options_ = { + auth: parsedUrl.auth, host: parsedUrl.hostname, path: parsedUrl.pathname, port: parsedUrl.port @@ -69,14 +82,20 @@ HttpClient.prototype.send = function(httpRequest, callback) { path += httpRequest.path; } - sendRequest({ + var options = { method: httpRequest.method, + auth: this.options_.auth, host: this.options_.host, port: this.options_.port, path: path, - headers: httpRequest.headers, - agent: agent - }, callback, data); + headers: httpRequest.headers + }; + + if (this.agent_) { + options.agent = this.agent_; + } + + sendRequest(options, callback, data, this.proxy_); }; @@ -86,15 +105,40 @@ HttpClient.prototype.send = function(httpRequest, callback) { * @param {function(Error, !webdriver.http.Response=)} callback The function to * invoke with the server's response. * @param {string=} opt_data The data to send with the request. + * @param {string=} opt_proxy The proxy server to use for the request. */ -var sendRequest = function(options, callback, opt_data) { +var sendRequest = function(options, callback, opt_data, opt_proxy) { + var host = options.host; + var port = options.port; + + if (opt_proxy) { + var proxy = url.parse(opt_proxy); + + options.headers['Host'] = options.host; + options.host = proxy.hostname; + options.port = proxy.port; + + if (proxy.auth) { + options.headers['Proxy-Authorization'] = + 'Basic ' + new Buffer(proxy.auth).toString('base64'); + } + } + var request = http.request(options, function(response) { if (response.statusCode == 302 || response.statusCode == 303) { - var location = url.parse(response.headers['location']); + try { + var location = url.parse(response.headers['location']); + } catch (ex) { + callback(Error( + 'Failed to parse "Location" header for server redirect: ' + + ex.message + '\nResponse was: \n' + + new HttpResponse(response.statusCode, response.headers, ''))); + return; + } if (!location.hostname) { - location.hostname = options.host; - location.port = options.port; + location.hostname = host; + location.port = port; } request.abort(); @@ -106,7 +150,7 @@ var sendRequest = function(options, callback, opt_data) { headers: { 'Accept': 'application/json; charset=utf-8' } - }, callback); + }, callback, undefined, opt_proxy); return; } @@ -122,7 +166,7 @@ var sendRequest = function(options, callback, opt_data) { request.on('error', function(e) { if (e.code === 'ECONNRESET') { setTimeout(function() { - sendRequest(options, callback, opt_data); + sendRequest(options, callback, opt_data, opt_proxy); }, 15); } else { var message = e.message; diff --git a/http/util.js b/http/util.js index fcb2219..fa35714 100644 --- a/http/util.js +++ b/http/util.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Various HTTP utilities. diff --git a/ie.js b/ie.js new file mode 100644 index 0000000..8aca222 --- /dev/null +++ b/ie.js @@ -0,0 +1,465 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Defines a {@linkplain Driver WebDriver} client for Microsoft's + * Internet Explorer. Before using the IEDriver, you must download the latest + * [IEDriverServer](http://selenium-release.storage.googleapis.com/index.html) + * and place it on your + * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). You must also apply + * the system configuration outlined on the Selenium project + * [wiki](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver) + */ + +'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'); + + +/** + * @const + * @final + */ +var IEDRIVER_EXE = 'IEDriverServer.exe'; + + + +/** + * IEDriverServer logging levels. + * @enum {string} + */ +var Level = { + FATAL: 'FATAL', + ERROR: 'ERROR', + WARN: 'WARN', + INFO: 'INFO', + DEBUG: 'DEBUG', + TRACE: 'TRACE' +}; + + + +/** + * Option keys: + * https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#ie-specific + * @enum {string} + */ +var Key = { + IGNORE_PROTECTED_MODE_SETTINGS: 'ignoreProtectedModeSettings', + IGNORE_ZOOM_SETTING: 'ignoreZoomSetting', + INITIAL_BROWSER_URL: 'initialBrowserUrl', + ENABLE_PERSISTENT_HOVER: 'enablePersistentHover', + ENABLE_ELEMENT_CACHE_CLEANUP: 'enableElementCacheCleanup', + REQUIRE_WINDOW_FOCUS: 'requireWindowFocus', + BROWSER_ATTACH_TIMEOUT: 'browserAttachTimeout', + FORCE_CREATE_PROCESS: 'ie.forceCreateProcessApi', + BROWSER_COMMAND_LINE_SWITCHES: 'ie.browserCommandLineSwitches', + USE_PER_PROCESS_PROXY: 'ie.usePerProcessProxy', + ENSURE_CLEAN_SESSION: 'ie.ensureCleanSession', + LOG_FILE: 'logFile', + LOG_LEVEL: 'logLevel', + HOST: 'host', + EXTRACT_PATH: 'extractPath', + SILENT: 'silent' +}; + + +/** + * Class for managing IEDriver specific options. + * @constructor + */ +var Options = function() { + /** @private {!Object<(boolean|number|string)>} */ + this.options_ = {}; + + /** @private {(webdriver.ProxyConfig|null)} */ + this.proxy_ = null; +}; + + + +/** + * Extracts the IEDriver specific options from the given capabilities + * object. + * @param {!webdriver.Capabilities} capabilities The capabilities object. + * @return {!Options} The IEDriver options. + */ +Options.fromCapabilities = function(capabilities) { + var options = new Options(); + var map = options.options_; + + Object.keys(Key).forEach(function(key) { + key = Key[key]; + if (capabilities.has(key)) { + map[key] = capabilities.get(key); + } + }); + + if (capabilities.has(webdriver.Capability.PROXY)) { + options.setProxy(capabilities.get(webdriver.Capability.PROXY)); + } + + return options; +}; + + +/** + * Whether to disable the protected mode settings check when the session is + * created. Disbling this setting may lead to significant instability as the + * browser may become unresponsive/hang. Only "best effort" support is provided + * when using this capability. + * + * For more information, refer to the IEDriver's + * [required system configuration](http://goo.gl/eH0Yi3). + * + * @param {boolean} ignoreSettings Whether to ignore protected mode settings. + * @return {!Options} A self reference. + */ +Options.prototype.introduceFlakinessByIgnoringProtectedModeSettings = + function(ignoreSettings) { + this.options_[Key.IGNORE_PROTECTED_MODE_SETTINGS] = !!ignoreSettings; + return this; + }; + + +/** + * Indicates whether to skip the check that the browser's zoom level is set to + * 100%. + * + * @parm {boolean} ignore Whether to ignore the browser's zoom level settings. + * @return {!Options} A self reference. + */ +Options.prototype.ignoreZoomSetting = function(ignore) { + this.options_[Key.IGNORE_ZOOM_SETTING] = !!ignore; + return this; +}; + + +/** + * Sets the initial URL loaded when IE starts. This is intended to be used with + * {@link #ignoreProtectedModeSettings} to allow the user to initialize IE in + * the proper Protected Mode zone. Setting this option may cause browser + * instability or flaky and unresponsive code. Only "best effort" support is + * provided when using this option. + * + * @param {string} url The initial browser URL. + * @return {!Options} A self reference. + */ +Options.prototype.initialBrowserUrl = function(url) { + this.options_[Key.INITIAL_BROWSER_URL] = url; + return this; +}; + + +/** + * Configures whether to enable persistent mouse hovering (true by default). + * Persistent hovering is achieved by continuously firing mouse over events at + * the last location the mouse cursor has been moved to. + * + * @param {boolean} enable Whether to enable persistent hovering. + * @return {!Options} A self reference. + */ +Options.prototype.enablePersistentHover = function(enable) { + this.options_[Key.ENABLE_PERSISTENT_HOVER] = !!enable; + return this; +}; + + +/** + * Configures whether the driver should attempt to remove obsolete + * {@linkplain webdriver.WebElement WebElements} from its internal cache on + * page navigation (true by default). Disabling this option will cause the + * driver to run with a larger memory footprint. + * + * @param {boolean} enable Whether to enable element reference cleanup. + * @return {!Options} A self reference. + */ +Options.prototype.enableElementCacheCleanup = function(enable) { + this.options_[Key.ENABLE_ELEMENT_CACHE_CLEANUP] = !!enable; + return this; +}; + + +/** + * Configures whether to require the IE window to have input focus before + * performing any user interactions (i.e. mouse or keyboard events). This + * option is disabled by default, but delivers much more accurate interaction + * events when enabled. + * + * @param {boolean} require Whether to require window focus. + * @return {!Options} A self reference. + */ +Options.prototype.requireWindowFocus = function(require) { + this.options_[Key.REQUIRE_WINDOW_FOCUS] = !!require; + return this; +}; + + +/** + * Configures the timeout, in milliseconds, that the driver will attempt to + * located and attach to a newly opened instance of Internet Explorer. The + * default is zero, which indicates waiting indefinitely. + * + * @param {number} timeout How long to wait for IE. + * @return {!Options} A self reference. + */ +Options.prototype.browserAttachTimeout = function(timeout) { + this.options_[Key.BROWSER_ATTACH_TIMEOUT] = Math.max(timeout, 0); + return this; +}; + + +/** + * Configures whether to launch Internet Explorer using the CreateProcess API. + * If this option is not specified, IE is launched using IELaunchURL, if + * available. For IE 8 and above, this option requires the TabProcGrowth + * registry value to be set to 0. + * + * @param {boolean} force Whether to use the CreateProcess API. + * @return {!Options} A self reference. + */ +Options.prototype.forceCreateProcessApi = function(force) { + this.options_[Key.FORCE_CREATE_PROCESS] = !!force; + return this; +}; + + +/** + * Specifies command-line switches to use when launching Internet Explorer. + * This is only valid when used with {@link #forceCreateProcessApi}. + * + * @param {...(string|!Array.)} var_args The arguments to add. + * @return {!Options} A self reference. + */ +Options.prototype.addArguments = function(var_args) { + var args = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || []; + args = args.concat.apply(args, arguments); + this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = args; + return this; +}; + + +/** + * Configures whether proxies should be configured on a per-process basis. If + * not set, setting a {@linkplain #setProxy proxy} will configure the system + * proxy. The default behavior is to use the system proxy. + * + * @param {boolean} enable Whether to enable per-process proxy settings. + * @return {!Options} A self reference. + */ +Options.prototype.usePerProcessProxy = function(enable) { + this.options_[Key.USE_PER_PROCESS_PROXY] = !!enable; + return this; +}; + + +/** + * Configures whether to clear the cache, cookies, history, and saved form data + * before starting the browser. _Using this capability will clear session data + * for all running instances of Internet Explorer, including those started + * manually._ + * + * @param {boolean} cleanSession Whether to clear all session data on startup. + * @return {!Options} A self reference. + */ +Options.prototype.ensureCleanSession = function(cleanSession) { + this.options_[Key.ENSURE_CLEAN_SESSION] = !!cleanSession; + return this; +}; + + +/** + * Sets the path to the log file the driver should log to. + * @param {string} path The log file path. + * @return {!Options} A self reference. + */ +Options.prototype.setLogFile = function(file) { + this.options_[Key.LOG_FILE] = file; + return this; +}; + + +/** + * Sets the IEDriverServer's logging {@linkplain Level level}. + * @param {Level} level The logging level. + * @return {!Options} A self reference. + */ +Options.prototype.setLogLevel = function(level) { + this.options_[Key.LOG_LEVEL] = level; + return this; +}; + + +/** + * Sets the IP address of the driver's host adapter. + * @param {string} host The IP address to use. + * @return {!Options} A self reference. + */ +Options.prototype.setHost = function(host) { + this.options_[Key.HOST] = host; + return this; +}; + + +/** + * Sets the path of the temporary data directory to use. + * @param {string} path The log file path. + * @return {!Options} A self reference. + */ +Options.prototype.setExtractPath = function(path) { + this.options_[Key.EXTRACT_PATH] = path; + return this; +}; + + +/** + * Sets whether the driver should start in silent mode. + * @param {boolean} silent Whether to run in silent mode. + * @return {!Options} A self reference. + */ +Options.prototype.silent = function(silent) { + this.options_[Key.SILENT] = silent; + return this; +}; + + +/** + * Sets the proxy settings for the new session. + * @param {webdriver.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ +Options.prototype.setProxy = function(proxy) { + this.proxy_ = proxy; + return this; +}; + + +/** + * Converts this options instance to a {@link webdriver.Capabilities} object. + * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge + * these options into, if any. + * @return {!webdriver.Capabilities} The capabilities. + */ +Options.prototype.toCapabilities = function(opt_capabilities) { + var capabilities = opt_capabilities || webdriver.Capabilities.ie(); + if (this.proxy_) { + capabilities.set(webdriver.Capability.PROXY, this.proxy_); + } + Object.keys(this.options_).forEach(function(key) { + capabilities.set(key, this.options_[key]); + }, this); + return capabilities; +}; + + +function createServiceFromCapabilities(capabilities) { + if (process.platform !== 'win32') { + throw Error( + 'The IEDriver may only be used on Windows, but you appear to be on ' + + process.platform + '. Did you mean to run against a remote ' + + 'WebDriver server?'); + } + + var exe = io.findInPath(IEDRIVER_EXE, true); + if (!fs.existsSync(exe)) { + throw Error('File does not exist: ' + exe); + } + + var args = []; + if (capabilities.has(Key.HOST)) { + args.push('--host=' + capabilities.get(Key.HOST)); + } + if (capabilities.has(Key.LOG_FILE)) { + args.push('--log-file=' + capabilities.get(Key.LOG_FILE)); + } + if (capabilities.has(Key.LOG_LEVEL)) { + args.push('--log-level=' + capabilities.get(Key.LOG_LEVEL)); + } + if (capabilities.has(Key.EXTRACT_PATH)) { + args.push('--extract-path=' + capabilities.get(Key.EXTRACT_PATH)); + } + if (capabilities.get(Key.SILENT)) { + args.push('--silent'); + } + + var port = portprober.findFreePort(); + return new remote.DriverService(exe, { + loopback: true, + port: port, + args: port.then(function(port) { + return args.concat('--port=' + port); + }), + stdio: 'ignore' + }); +} + + +/** + * A WebDriver client for Microsoft's Internet Explorer. + * + * @param {(webdriver.Capabilities|Options)=} opt_config The configuration + * options. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or + * {@code null} to use the currently active flow. + * @constructor + * @extends {webdriver.WebDriver} + */ +var Driver = function(opt_config, opt_flow) { + var capabilities = opt_config instanceof Options ? + opt_config.toCapabilities() : + (opt_config || webdriver.Capabilities.ie()); + + var service = createServiceFromCapabilities(capabilities); + var executor = executors.createExecutor(service.start()); + var driver = webdriver.WebDriver.createSession( + executor, capabilities, opt_flow); + + webdriver.WebDriver.call( + this, driver.getSession(), executor, driver.controlFlow()); + + var boundQuit = this.quit.bind(this); + + /** @override */ + this.quit = function() { + return boundQuit().thenFinally(service.kill.bind(service)); + }; +}; +util.inherits(Driver, webdriver.WebDriver); + + +/** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ +Driver.prototype.setFileDetector = function() { +}; + + +// PUBLIC API + + +exports.Driver = Driver; +exports.Options = Options; +exports.Level = Level; diff --git a/index.js b/index.js index 7391fdb..b45b560 100644 --- a/index.js +++ b/index.js @@ -1,17 +1,19 @@ -// Copyright 2012 Selenium committers -// Copyright 2012 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 main user facing module. Exports WebDriver's primary @@ -52,6 +54,14 @@ exports.Command = base.require('webdriver.Command'); exports.EventEmitter = base.require('webdriver.EventEmitter'); +/** @type {function(new: webdriver.FileDetector)} */ +exports.FileDetector = base.require('webdriver.FileDetector'); + + +/** @type {function(new: webdriver.Serializable)} */ +exports.Serializable = base.require('webdriver.Serializable'); + + /** @type {function(new: webdriver.Session)} */ exports.Session = base.require('webdriver.Session'); @@ -64,6 +74,10 @@ exports.WebDriver = base.require('webdriver.WebDriver'); exports.WebElement = base.require('webdriver.WebElement'); +/** @type {function(new: webdriver.WebElementPromise)} */ +exports.WebElementPromise = base.require('webdriver.WebElementPromise'); + + // Export the remainder of our API through getters to keep things cleaner // when this module is used in a REPL environment. @@ -126,3 +140,9 @@ exports.WebElement = base.require('webdriver.WebElement'); (exports.__defineGetter__('stacktrace', function() { return base.exportPublicApi('webdriver.stacktrace'); })); + + +/** @type {webdriver.until.} */ +(exports.__defineGetter__('until', function() { + return base.exportPublicApi('webdriver.until'); +})); diff --git a/io/exec.js b/io/exec.js new file mode 100644 index 0000000..187ea78 --- /dev/null +++ b/io/exec.js @@ -0,0 +1,140 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 childProcess = require('child_process'); + +var promise = require('..').promise; + + +/** + * A hash with configuration options for an executed command. + * + * - `args` - Command line arguments. + * - `env` - Command environment; will inherit from the current process if + * missing. + * - `stdio` - IO configuration for the spawned server process. For more + * information, refer to the documentation of `child_process.spawn`. + * + * @typedef {{ + * args: (!Array.|undefined), + * env: (!Object.|undefined), + * stdio: (string|!Array.|undefined) + * }} + */ +var Options; + + +/** + * Describes a command's termination conditions. + * @param {?number} code The exit code, or {@code null} if the command did not + * exit normally. + * @param {?string} signal The signal used to kill the command, or + * {@code null}. + * @constructor + */ +var Result = function(code, signal) { + /** @type {?number} */ + this.code = code; + + /** @type {?string} */ + this.signal = signal; +}; + + +/** @override */ +Result.prototype.toString = function() { + return 'Result(code=' + this.code + ', signal=' + this.signal + ')'; +}; + + + +/** + * Represents a command running in a sub-process. + * @param {!promise.Promise.} result The command result. + * @constructor + */ +var Command = function(result, onKill) { + /** @return {boolean} Whether this command is still running. */ + this.isRunning = function() { + return result.isPending(); + }; + + /** + * @return {!promise.Promise.} A promise for the result of this + * command. + */ + this.result = function() { + return result; + }; + + /** + * Sends a signal to the underlying process. + * @param {string=} opt_signal The signal to send; defaults to + * {@code SIGTERM}. + */ + this.kill = function(opt_signal) { + onKill(opt_signal || 'SIGTERM'); + }; +}; + + +// PUBLIC API + + +/** + * Spawns a child process. The returned {@link Command} may be used to wait + * for the process result or to send signals to the process. + * + * @param {string} command The executable to spawn. + * @param {Options=} opt_options The command options. + * @return {!Command} The launched command. + */ +module.exports = function(command, opt_options) { + var options = opt_options || {}; + + var proc = childProcess.spawn(command, options.args || [], { + env: options.env || process.env, + stdio: options.stdio || 'ignore' + }).once('exit', onExit); + + // This process should not wait on the spawned child, however, we do + // want to ensure the child is killed when this process exits. + proc.unref(); + process.once('exit', killCommand); + + var result = promise.defer(); + var cmd = new Command(result.promise, function(signal) { + if (!result.isPending() || !proc) { + return; // No longer running. + } + proc.kill(signal); + }); + return cmd; + + function onExit(code, signal) { + proc = null; + process.removeListener('exit', killCommand); + result.fulfill(new Result(code, signal)); + } + + function killCommand() { + process.removeListener('exit', killCommand); + proc && proc.kill('SIGTERM'); + } +}; diff --git a/io/index.js b/io/index.js index 5d5a3de..3077c46 100644 --- a/io/index.js +++ b/io/index.js @@ -1,28 +1,197 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 fs = require('fs'), - path = require('path'); + path = require('path'), + rimraf = require('rimraf'), + tmp = require('tmp'); +var promise = require('..').promise; -var PATH_SEPARATOR = process.platform === 'win32' ? ';' : ':'; // PUBLIC API + +/** + * Recursively removes a directory and all of its contents. This is equivalent + * to {@code rm -rf} on a POSIX system. + * @param {string} path Path to the directory to remove. + * @return {!promise.Promise} A promise to be resolved when the operation has + * completed. + */ +exports.rmDir = function(path) { + return new promise.Promise(function(fulfill, reject) { + var numAttempts = 0; + attemptRm(); + function attemptRm() { + numAttempts += 1; + rimraf(path, function(err) { + if (err) { + if (err.code === 'ENOTEMPTY' && numAttempts < 2) { + attemptRm(); + return; + } + reject(err); + } else { + fulfill(); + } + }); + } + }); +}; + + +/** + * Copies one file to another. + * @param {string} src The source file. + * @param {string} dst The destination file. + * @return {!promise.Promise.} A promise for the copied file's path. + */ +exports.copy = function(src, dst) { + var copied = promise.defer(); + + var rs = fs.createReadStream(src); + rs.on('error', copied.reject); + rs.on('end', function() { + copied.fulfill(dst); + }); + + var ws = fs.createWriteStream(dst); + ws.on('error', copied.reject); + + rs.pipe(ws); + + return copied.promise; +}; + + +/** + * Recursively copies the contents of one directory to another. + * @param {string} src The source directory to copy. + * @param {string} dst The directory to copy into. + * @param {(RegEx|function(string): boolean)=} opt_exclude An exclusion filter + * as either a regex or predicate function. All files matching this filter + * will not be copied. + * @return {!promise.Promise.} A promise for the destination + * directory's path once all files have been copied. + */ +exports.copyDir = function(src, dst, opt_exclude) { + var predicate = opt_exclude; + if (opt_exclude && typeof opt_exclude !== 'function') { + predicate = function(p) { + return !opt_exclude.test(p); + }; + } + + // TODO(jleyba): Make this function completely async. + if (!fs.existsSync(dst)) { + fs.mkdirSync(dst); + } + + var files = fs.readdirSync(src); + files = files.map(function(file) { + return path.join(src, file); + }); + + if (predicate) { + files = files.filter(predicate); + } + + var results = []; + files.forEach(function(file) { + var stats = fs.statSync(file); + var target = path.join(dst, path.basename(file)); + + if (stats.isDirectory()) { + if (!fs.existsSync(target)) { + fs.mkdirSync(target, stats.mode); + } + results.push(exports.copyDir(file, target, predicate)); + } else { + results.push(exports.copy(file, target)); + } + }); + + return promise.all(results).then(function() { + return dst; + }); +}; + + +/** + * Tests if a file path exists. + * @param {string} path The path to test. + * @return {!promise.Promise.} A promise for whether the file exists. + */ +exports.exists = function(path) { + var result = promise.defer(); + fs.exists(path, result.fulfill); + return result.promise; +}; + + +/** + * Deletes a name from the filesystem and possibly the file it refers to. Has + * no effect if the file does not exist. + * @param {string} path The path to remove. + * @return {!promise.Promise} A promise for when the file has been removed. + */ +exports.unlink = function(path) { + return new promise.Promise(function(fulfill, reject) { + fs.exists(path, function(exists) { + if (exists) { + fs.unlink(path, function(err) { + err && reject(err) || fulfill(); + }); + } else { + fulfill(); + } + }); + }); +}; + + +/** + * @return {!promise.Promise.} A promise for the path to a temporary + * directory. + * @see https://www.npmjs.org/package/tmp + */ +exports.tmpDir = function() { + return promise.checkedNodeCall(tmp.dir); +}; + + +/** + * @param {{postfix: string}=} opt_options Temporary file options. + * @return {!promise.Promise.} A promise for the path to a temporary + * file. + * @see https://www.npmjs.org/package/tmp + */ +exports.tmpFile = function(opt_options) { + // |tmp.file| checks arguments length to detect options rather than doing a + // truthy check, so we must only pass options if there are some to pass. + return opt_options ? + promise.checkedNodeCall(tmp.file, opt_options) : + promise.checkedNodeCall(tmp.file); +}; + + /** * Searches the {@code PATH} environment variable for the given file. * @param {string} file The file to locate on the PATH. @@ -40,7 +209,7 @@ exports.findInPath = function(file, opt_checkCwd) { } } - var dirs = process.env['PATH'].split(PATH_SEPARATOR); + var dirs = process.env['PATH'].split(path.delimiter); var found = null; dirs.forEach(function(dir) { var tmp = path.join(dir, file); diff --git a/lib/atoms/error.js b/lib/atoms/error.js index 5b23ff9..6fb2e1f 100644 --- a/lib/atoms/error.js +++ b/lib/atoms/error.js @@ -1,21 +1,23 @@ -// Copyright 2010 WebDriver committers -// Copyright 2010 Google Inc. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 errors as defined by WebDriver's - * wire protocol: http://code.google.com/p/selenium/wiki/JsonWireProtocol. + * wire protocol: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol */ goog.provide('bot.Error'); @@ -23,8 +25,8 @@ goog.provide('bot.ErrorCode'); /** - * Error codes from the WebDriver wire protocol: - * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes + * Error codes from the Selenium WebDriver protocol: + * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#response-status-codes * * @enum {number} */ @@ -46,8 +48,8 @@ bot.ErrorCode = { NO_SUCH_WINDOW: 23, INVALID_COOKIE_DOMAIN: 24, UNABLE_TO_SET_COOKIE: 25, - MODAL_DIALOG_OPENED: 26, - NO_MODAL_DIALOG_OPEN: 27, + UNEXPECTED_ALERT_OPEN: 26, + NO_SUCH_ALERT: 27, SCRIPT_TIMEOUT: 28, INVALID_ELEMENT_COORDINATES: 29, IME_NOT_AVAILABLE: 30, @@ -63,11 +65,8 @@ bot.ErrorCode = { }; - /** - * Error extension that includes error status codes from the WebDriver wire - * protocol: - * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes + * Represents an error returned from a WebDriver command request. * * @param {!bot.ErrorCode} code The error's status code. * @param {string=} opt_message Optional error message. @@ -116,35 +115,34 @@ goog.inherits(bot.Error, Error); /** - * Status strings enumerated in the W3C WebDriver working draft. + * Status strings enumerated in the W3C WebDriver protocol. * @enum {string} - * @see http://www.w3.org/TR/webdriver/#status-codes + * @see https://w3c.github.io/webdriver/webdriver-spec.html#handling-errors */ bot.Error.State = { ELEMENT_NOT_SELECTABLE: 'element not selectable', ELEMENT_NOT_VISIBLE: 'element not visible', - IME_ENGINE_ACTIVATION_FAILED: 'ime engine activation failed', - IME_NOT_AVAILABLE: 'ime not available', + INVALID_ARGUMENT: 'invalid argument', INVALID_COOKIE_DOMAIN: 'invalid cookie domain', INVALID_ELEMENT_COORDINATES: 'invalid element coordinates', INVALID_ELEMENT_STATE: 'invalid element state', INVALID_SELECTOR: 'invalid selector', + INVALID_SESSION_ID: 'invalid session id', JAVASCRIPT_ERROR: 'javascript error', MOVE_TARGET_OUT_OF_BOUNDS: 'move target out of bounds', NO_SUCH_ALERT: 'no such alert', - NO_SUCH_DOM: 'no such dom', NO_SUCH_ELEMENT: 'no such element', NO_SUCH_FRAME: 'no such frame', NO_SUCH_WINDOW: 'no such window', SCRIPT_TIMEOUT: 'script timeout', SESSION_NOT_CREATED: 'session not created', STALE_ELEMENT_REFERENCE: 'stale element reference', - SUCCESS: 'success', TIMEOUT: 'timeout', UNABLE_TO_SET_COOKIE: 'unable to set cookie', UNEXPECTED_ALERT_OPEN: 'unexpected alert open', UNKNOWN_COMMAND: 'unknown command', UNKNOWN_ERROR: 'unknown error', + UNKNOWN_METHOD: 'unknown method', UNSUPPORTED_OPERATION: 'unsupported operation' }; @@ -161,8 +159,8 @@ goog.scope(function() { map[code.ELEMENT_NOT_SELECTABLE] = state.ELEMENT_NOT_SELECTABLE; map[code.ELEMENT_NOT_VISIBLE] = state.ELEMENT_NOT_VISIBLE; - map[code.IME_ENGINE_ACTIVATION_FAILED] = state.IME_ENGINE_ACTIVATION_FAILED; - map[code.IME_NOT_AVAILABLE] = state.IME_NOT_AVAILABLE; + map[code.IME_ENGINE_ACTIVATION_FAILED] = state.UNKNOWN_ERROR; + map[code.IME_NOT_AVAILABLE] = state.UNKNOWN_ERROR; map[code.INVALID_COOKIE_DOMAIN] = state.INVALID_COOKIE_DOMAIN; map[code.INVALID_ELEMENT_COORDINATES] = state.INVALID_ELEMENT_COORDINATES; map[code.INVALID_ELEMENT_STATE] = state.INVALID_ELEMENT_STATE; @@ -172,17 +170,16 @@ goog.scope(function() { map[code.JAVASCRIPT_ERROR] = state.JAVASCRIPT_ERROR; map[code.METHOD_NOT_ALLOWED] = state.UNSUPPORTED_OPERATION; map[code.MOVE_TARGET_OUT_OF_BOUNDS] = state.MOVE_TARGET_OUT_OF_BOUNDS; - map[code.NO_MODAL_DIALOG_OPEN] = state.NO_SUCH_ALERT; + map[code.NO_SUCH_ALERT] = state.NO_SUCH_ALERT; map[code.NO_SUCH_ELEMENT] = state.NO_SUCH_ELEMENT; map[code.NO_SUCH_FRAME] = state.NO_SUCH_FRAME; map[code.NO_SUCH_WINDOW] = state.NO_SUCH_WINDOW; map[code.SCRIPT_TIMEOUT] = state.SCRIPT_TIMEOUT; map[code.SESSION_NOT_CREATED] = state.SESSION_NOT_CREATED; map[code.STALE_ELEMENT_REFERENCE] = state.STALE_ELEMENT_REFERENCE; - map[code.SUCCESS] = state.SUCCESS; map[code.TIMEOUT] = state.TIMEOUT; map[code.UNABLE_TO_SET_COOKIE] = state.UNABLE_TO_SET_COOKIE; - map[code.MODAL_DIALOG_OPENED] = state.UNEXPECTED_ALERT_OPEN; + map[code.UNEXPECTED_ALERT_OPEN] = state.UNEXPECTED_ALERT_OPEN map[code.UNKNOWN_ERROR] = state.UNKNOWN_ERROR; map[code.UNSUPPORTED_OPERATION] = state.UNKNOWN_COMMAND; }); // goog.scope diff --git a/lib/atoms/json.js b/lib/atoms/json.js index b3e863c..24e1c77 100644 --- a/lib/atoms/json.js +++ b/lib/atoms/json.js @@ -1,17 +1,19 @@ -// Copyright 2012 WebDriver committers -// Copyright 2012 Google Inc. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Provides JSON utilities that uses native JSON parsing where @@ -47,10 +49,10 @@ bot.json.NATIVE_JSON = true; * @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 || + // List WebKit first since every supported version 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.GECKO && bot.userAgent.isEngineVersion(3.5)) || (goog.userAgent.IE && bot.userAgent.isEngineVersion(8)); diff --git a/lib/atoms/response.js b/lib/atoms/response.js index d929a66..b14aeeb 100644 --- a/lib/atoms/response.js +++ b/lib/atoms/response.js @@ -1,20 +1,23 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 + * @see: hhttps://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#responses */ goog.provide('bot.response'); @@ -27,7 +30,7 @@ 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 + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#responses */ bot.response.ResponseObject; @@ -87,7 +90,7 @@ bot.response.createErrorResponse = function(error) { * 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 + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#failed-commands */ bot.response.checkResponse = function(responseObj) { var status = responseObj['status']; diff --git a/lib/atoms/userAgent.js b/lib/atoms/userAgent.js index 60964d0..f4a75c2 100644 --- a/lib/atoms/userAgent.js +++ b/lib/atoms/userAgent.js @@ -1,17 +1,19 @@ -// Copyright 2011 WebDriver committers -// Copyright 2011 Google Inc. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 diff --git a/lib/firefox/amd64/libnoblur64.so b/lib/firefox/amd64/libnoblur64.so new file mode 100644 index 0000000..916e530 Binary files /dev/null and b/lib/firefox/amd64/libnoblur64.so differ diff --git a/lib/firefox/i386/libnoblur.so b/lib/firefox/i386/libnoblur.so new file mode 100644 index 0000000..8e7db8d Binary files /dev/null and b/lib/firefox/i386/libnoblur.so differ diff --git a/lib/firefox/webdriver.json b/lib/firefox/webdriver.json new file mode 100644 index 0000000..14ba421 --- /dev/null +++ b/lib/firefox/webdriver.json @@ -0,0 +1,73 @@ +{ + "frozen": { + "app.update.auto": false, + "app.update.enabled": false, + "browser.displayedE10SNotice": 4, + "browser.download.manager.showWhenStarting": false, + "browser.EULA.override": true, + "browser.EULA.3.accepted": true, + "browser.link.open_external": 2, + "browser.link.open_newwindow": 2, + "browser.offline": false, + "browser.reader.detectedFirstArticle": true, + "browser.safebrowsing.enabled": false, + "browser.safebrowsing.malware.enabled": false, + "browser.search.update": false, + "browser.selfsupport.url" : "", + "browser.sessionstore.resume_from_crash": false, + "browser.shell.checkDefaultBrowser": false, + "browser.tabs.warnOnClose": false, + "browser.tabs.warnOnOpen": false, + "datareporting.healthreport.service.enabled": false, + "datareporting.healthreport.uploadEnabled": false, + "datareporting.healthreport.service.firstRun": false, + "datareporting.healthreport.logging.consoleEnabled": false, + "datareporting.policy.dataSubmissionEnabled": false, + "datareporting.policy.dataSubmissionPolicyAccepted": false, + "devtools.errorconsole.enabled": true, + "dom.disable_open_during_load": false, + "extensions.autoDisableScopes": 10, + "extensions.blocklist.enabled": false, + "extensions.logging.enabled": true, + "extensions.update.enabled": false, + "extensions.update.notifyUser": false, + "javascript.enabled": true, + "network.manage-offline-status": false, + "network.http.phishy-userpass-length": 255, + "offline-apps.allow_by_default": true, + "prompts.tab_modal.enabled": false, + "security.csp.enable": false, + "security.fileuri.origin_policy": 3, + "security.fileuri.strict_origin_policy": false, + "security.warn_entering_secure": false, + "security.warn_entering_secure.show_once": false, + "security.warn_entering_weak": false, + "security.warn_entering_weak.show_once": false, + "security.warn_leaving_secure": false, + "security.warn_leaving_secure.show_once": false, + "security.warn_submit_insecure": false, + "security.warn_viewing_mixed": false, + "security.warn_viewing_mixed.show_once": false, + "signon.rememberSignons": false, + "toolkit.networkmanager.disable": true, + "toolkit.telemetry.prompted": 2, + "toolkit.telemetry.enabled": false, + "toolkit.telemetry.rejected": true, + "xpinstall.signatures.required": false + }, + "mutable": { + "browser.dom.window.dump.enabled": true, + "browser.newtab.url": "about:blank", + "browser.newtabpage.enabled": false, + "browser.startup.page": 0, + "browser.startup.homepage": "about:blank", + "dom.max_chrome_script_run_time": 30, + "dom.max_script_run_time": 30, + "dom.report_all_js_exceptions": true, + "javascript.options.showInConsole": true, + "network.http.max-connections-per-server": 10, + "startup.homepage_welcome_url": "about:blank", + "webdriver_accept_untrusted_certs": true, + "webdriver_assume_untrusted_issuer": true + } +} diff --git a/lib/firefox/webdriver.xpi b/lib/firefox/webdriver.xpi new file mode 100644 index 0000000..198bc83 Binary files /dev/null and b/lib/firefox/webdriver.xpi differ diff --git a/lib/goog/array/array.js b/lib/goog/array/array.js index 5eea925..990a6d5 100644 --- a/lib/goog/array/array.js +++ b/lib/goog/array/array.js @@ -15,6 +15,7 @@ /** * @fileoverview Utilities for manipulating arrays. * + * @author arv@google.com (Erik Arvidsson) */ @@ -42,6 +43,14 @@ goog.require('goog.asserts'); 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}} */ @@ -50,17 +59,29 @@ goog.array.ArrayLike; /** * Returns the last element in an array without removing it. - * @param {goog.array.ArrayLike} array The array. - * @return {*} Last item in array. + * Same as goog.array.last. + * @param {Array|goog.array.ArrayLike} array The array. + * @return {T} Last item in array. + * @template T */ goog.array.peek = function(array) { return array[array.length - 1]; }; +/** + * Returns the last element in an array without removing it. + * Same as goog.array.peek. + * @param {Array|goog.array.ArrayLike} array The array. + * @return {T} Last item in array. + * @template T + */ +goog.array.last = goog.array.peek; + + /** * Reference to the original {@code Array.prototype}. - * @private + * @private {!Object} */ goog.array.ARRAY_PROTOTYPE_ = Array.prototype; @@ -73,19 +94,21 @@ goog.array.ARRAY_PROTOTYPE_ = Array.prototype; /** - * 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. + * 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 {@link http://tinyurl.com/developer-mozilla-org-array-indexof} * - * @param {goog.array.ArrayLike} arr The array to be searched. - * @param {*} obj The object for which we are searching. + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {T} obj The object for which we are searching. * @param {number=} opt_fromIndex The index at which to start the search. If * omitted the search starts at index 0. * @return {number} The index of the first matching array element. + * @template T */ goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && - goog.array.ARRAY_PROTOTYPE_.indexOf ? + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.indexOf) ? function(arr, obj, opt_fromIndex) { goog.asserts.assert(arr.length != null); @@ -118,14 +141,16 @@ goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof} * - * @param {goog.array.ArrayLike} arr The array to be searched. - * @param {*} obj The object for which we are searching. + * @param {!Array|!goog.array.ArrayLike} arr The array to be searched. + * @param {T} obj The object for which we are searching. * @param {?number=} opt_fromIndex The index at which to start the search. If * omitted the search starts at the end of the array. * @return {number} The index of the last matching array element. + * @template T */ goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && - goog.array.ARRAY_PROTOTYPE_.lastIndexOf ? + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.lastIndexOf) ? function(arr, obj, opt_fromIndex) { goog.asserts.assert(arr.length != null); @@ -161,7 +186,7 @@ goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && * Calls a function for each element in an array. Skips holes in the array. * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach} * - * @param {Array.|goog.array.ArrayLike} arr Array or array like object over + * @param {Array|goog.array.ArrayLike} arr Array or array like object over * which to iterate. * @param {?function(this: S, T, number, ?): ?} f The function to call for every * element. This function takes 3 arguments (the element, the index and the @@ -170,7 +195,8 @@ goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && * @template T,S */ goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES && - goog.array.ARRAY_PROTOTYPE_.forEach ? + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.forEach) ? function(arr, f, opt_obj) { goog.asserts.assert(arr.length != null); @@ -191,7 +217,7 @@ goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES && * Calls a function for each element in an array, starting from the last * element rather than the first. * - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this: S, T, number, ?): ?} f The function to call for every * element. This function @@ -218,7 +244,7 @@ goog.array.forEachRight = function(arr, f, opt_obj) { * * See {@link http://tinyurl.com/developer-mozilla-org-array-filter} * - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?):boolean} f The function to call for * every element. This function @@ -227,12 +253,13 @@ goog.array.forEachRight = function(arr, f, opt_obj) { * result array. If it is false the element is not included. * @param {S=} opt_obj The object to be used as the value of 'this' * within f. - * @return {!Array} a new array in which only elements that passed the test are - * present. + * @return {!Array} a new array in which only elements that passed the test + * are present. * @template T,S */ goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES && - goog.array.ARRAY_PROTOTYPE_.filter ? + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.filter) ? function(arr, f, opt_obj) { goog.asserts.assert(arr.length != null); @@ -261,19 +288,19 @@ goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-map} * - * @param {Array.|goog.array.ArrayLike} arr Array or array - * like object over which to iterate. - * @param {?function(this:S, T, number, ?):?} f The function to call for every - * element. This function - * takes 3 arguments (the element, the index and the array) and should - * return something. The result will be inserted into a new array. - * @param {S=} opt_obj The object to be used as the value of 'this' - * within f. - * @return {!Array} a new array with the results from f. - * @template T,S + * @param {Array|goog.array.ArrayLike} arr Array or array like object + * over which to iterate. + * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call + * for every element. This function takes 3 arguments (the element, + * the index and the array) and should return something. The result will be + * inserted into a new array. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within f. + * @return {!Array} a new array with the results from f. + * @template THIS, VALUE, RESULT */ goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && - goog.array.ARRAY_PROTOTYPE_.map ? + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.map) ? function(arr, f, opt_obj) { goog.asserts.assert(arr.length != null); @@ -302,9 +329,9 @@ goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0); * returns 10 * - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. - * @param {?function(this:S, R, T, number, ?) : R} f The function to call for + * @param {function(this:S, R, T, number, ?) : R} f The function to call for * every element. This function * takes 4 arguments (the function's previous result or the initial value, * the value of the current array element, the current array index, and the @@ -316,20 +343,23 @@ goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && * @return {R} Result of evaluating f repeatedly across the values of the array. * @template T,S,R */ -goog.array.reduce = function(arr, f, val, opt_obj) { - if (arr.reduce) { - if (opt_obj) { - return arr.reduce(goog.bind(f, opt_obj), val); - } else { - return arr.reduce(f, val); - } - } - var rval = val; - goog.array.forEach(arr, function(val, index) { - rval = f.call(opt_obj, rval, val, index, arr); - }); - return rval; -}; +goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.reduce) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return goog.array.ARRAY_PROTOTYPE_.reduce.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEach(arr, function(val, index) { + rval = f.call(opt_obj, rval, val, index, arr); + }); + return rval; + }; /** @@ -343,7 +373,7 @@ goog.array.reduce = function(arr, f, val, opt_obj) { * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, ''); * returns 'cba' * - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, R, T, number, ?) : R} f The function to call for * every element. This function @@ -358,20 +388,23 @@ goog.array.reduce = function(arr, f, val, opt_obj) { * values of the array. * @template T,S,R */ -goog.array.reduceRight = function(arr, f, val, opt_obj) { - if (arr.reduceRight) { - if (opt_obj) { - return arr.reduceRight(goog.bind(f, opt_obj), val); - } else { - return arr.reduceRight(f, val); - } - } - var rval = val; - goog.array.forEachRight(arr, function(val, index) { - rval = f.call(opt_obj, rval, val, index, arr); - }); - return rval; -}; +goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.reduceRight) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return goog.array.ARRAY_PROTOTYPE_.reduceRight.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEachRight(arr, function(val, index) { + rval = f.call(opt_obj, rval, val, index, arr); + }); + return rval; + }; /** @@ -381,7 +414,7 @@ goog.array.reduceRight = function(arr, f, val, opt_obj) { * * See {@link http://tinyurl.com/developer-mozilla-org-array-some} * - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call for * for every element. This function takes 3 arguments (the element, the @@ -392,7 +425,8 @@ goog.array.reduceRight = function(arr, f, val, opt_obj) { * @template T,S */ goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && - goog.array.ARRAY_PROTOTYPE_.some ? + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.some) ? function(arr, f, opt_obj) { goog.asserts.assert(arr.length != null); @@ -417,7 +451,7 @@ goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && * * See {@link http://tinyurl.com/developer-mozilla-org-array-every} * - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call for * for every element. This function takes 3 arguments (the element, the @@ -428,7 +462,8 @@ goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && * @template T,S */ goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES && - goog.array.ARRAY_PROTOTYPE_.every ? + (goog.array.ASSUME_NATIVE_FUNCTIONS || + goog.array.ARRAY_PROTOTYPE_.every) ? function(arr, f, opt_obj) { goog.asserts.assert(arr.length != null); @@ -450,7 +485,7 @@ goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES && * Counts the array elements that fulfill the predicate, i.e. for which the * callback function returns true. Skips holes in the array. * - * @param {!(Array.|goog.array.ArrayLike)} arr Array or array like object + * @param {!(Array|goog.array.ArrayLike)} arr Array or array like object * over which to iterate. * @param {function(this: S, T, number, ?): boolean} f The function to call for * every element. Takes 3 arguments (the element, the index and the array). @@ -472,13 +507,13 @@ goog.array.count = function(arr, f, opt_obj) { /** * Search an array for the first element that satisfies a given condition and * return that element. - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function takes 3 arguments (the element, the * index and the array) and should return a boolean. * @param {S=} opt_obj An optional "this" context for the function. - * @return {T} The first array element that passes the test, or null if no + * @return {T|null} The first array element that passes the test, or null if no * element is found. * @template T,S */ @@ -491,7 +526,7 @@ goog.array.find = function(arr, f, opt_obj) { /** * Search an array for the first element that satisfies a given condition and * return its index. - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call for * every element. This function @@ -517,14 +552,14 @@ goog.array.findIndex = function(arr, f, opt_obj) { /** * Search an array (in reverse order) for the last element that satisfies a * given condition and return that element. - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function * takes 3 arguments (the element, the index and the array) and should * return a boolean. * @param {S=} opt_obj An optional "this" context for the function. - * @return {T} The last array element that passes the test, or null if no + * @return {T|null} The last array element that passes the test, or null if no * element is found. * @template T,S */ @@ -537,13 +572,13 @@ goog.array.findRight = function(arr, f, opt_obj) { /** * Search an array (in reverse order) for the last element that satisfies a * given condition and return its index. - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function * takes 3 arguments (the element, the index and the array) and should * return a boolean. - * @param {Object=} opt_obj An optional "this" context for the function. + * @param {S=} opt_obj An optional "this" context for the function. * @return {number} The index of the last array element that passes the test, * or -1 if no element is found. * @template T,S @@ -600,7 +635,7 @@ goog.array.clear = function(arr) { /** * Pushes an item into an array, if it's not already in the array. - * @param {Array.} arr Array into which to insert the item. + * @param {Array} arr Array into which to insert the item. * @param {T} obj Value to add. * @template T */ @@ -637,7 +672,7 @@ goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) { /** * Inserts an object into an array before a specified object. - * @param {Array.} arr The array to modify. + * @param {Array} arr The array to modify. * @param {T} obj The object to insert. * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2 * is omitted or not found, obj is inserted at the end of the array. @@ -655,9 +690,11 @@ goog.array.insertBefore = function(arr, obj, opt_obj2) { /** * Removes the first occurrence of a particular value from an array. - * @param {goog.array.ArrayLike} arr Array from which to remove value. - * @param {*} obj Object to remove. + * @param {Array|goog.array.ArrayLike} arr Array from which to remove + * value. + * @param {T} obj Object to remove. * @return {boolean} True if an element was removed. + * @template T */ goog.array.remove = function(arr, obj) { var i = goog.array.indexOf(arr, obj); @@ -688,7 +725,7 @@ goog.array.removeAt = function(arr, i) { /** * Removes the first value that satisfies the given condition. - * @param {Array.|goog.array.ArrayLike} arr Array or array + * @param {Array|goog.array.ArrayLike} arr Array or array * like object over which to iterate. * @param {?function(this:S, T, number, ?) : boolean} f The function to call * for every element. This function @@ -708,6 +745,31 @@ goog.array.removeIf = function(arr, f, opt_obj) { }; +/** + * Removes all values that satisfy the given condition. + * @param {Array|goog.array.ArrayLike} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The number of items removed + * @template T,S + */ +goog.array.removeAllIf = function(arr, f, opt_obj) { + var removedCount = 0; + goog.array.forEachRight(arr, function(val, index) { + if (f.call(opt_obj, val, index, arr)) { + if (goog.array.removeAt(arr, index)) { + removedCount++; + } + } + }); + return removedCount; +}; + + /** * 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 @@ -733,7 +795,7 @@ goog.array.removeIf = function(arr, f, opt_obj) { * * @param {...*} var_args Items to concatenate. Arrays will have each item * added, while primitives and objects will be added as is. - * @return {!Array} The new resultant array. + * @return {!Array} The new resultant array. */ goog.array.concat = function(var_args) { return goog.array.ARRAY_PROTOTYPE_.concat.apply( @@ -741,13 +803,27 @@ goog.array.concat = function(var_args) { }; +/** + * Returns a new array that contains the contents of all the arrays passed. + * @param {...!Array} var_args + * @return {!Array} + * @template T + */ +goog.array.join = function(var_args) { + return goog.array.ARRAY_PROTOTYPE_.concat.apply( + goog.array.ARRAY_PROTOTYPE_, arguments); +}; + + /** * Converts an object to an array. - * @param {goog.array.ArrayLike} object The object to convert to an array. - * @return {!Array} The object converted into an array. If object has a + * @param {Array|goog.array.ArrayLike} object The object to convert to an + * array. + * @return {!Array} 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. + * @template T */ goog.array.toArray = function(object) { var length = object.length; @@ -768,8 +844,10 @@ goog.array.toArray = function(object) { /** * Does a shallow copy of an array. - * @param {goog.array.ArrayLike} arr Array or array-like object to clone. - * @return {!Array} Clone of the input array. + * @param {Array|goog.array.ArrayLike} arr Array or array-like object to + * clone. + * @return {!Array} Clone of the input array. + * @template T */ goog.array.clone = goog.array.toArray; @@ -785,28 +863,18 @@ goog.array.clone = goog.array.toArray; * goog.array.extend(a, 2); * a; // [0, 1, 2] * - * @param {Array} arr1 The array to modify. - * @param {...*} var_args The elements or arrays of elements to add to arr1. + * @param {Array} arr1 The array to modify. + * @param {...(Array|VALUE)} var_args The elements or arrays of elements + * to add to arr1. + * @template VALUE */ goog.array.extend = function(arr1, var_args) { for (var i = 1; i < arguments.length; i++) { var arr2 = arguments[i]; - // If we have an Array or an Arguments object we can just call push - // directly. - var isArrayLike; - if (goog.isArray(arr2) || - // Detect Arguments. ES5 says that the [[Class]] of an Arguments object - // is "Arguments" but only V8 and JSC/Safari gets this right. We instead - // detect Arguments by checking for array like and presence of "callee". - (isArrayLike = goog.isArrayLike(arr2)) && - // The getter for callee throws an exception in strict mode - // according to section 10.6 in ES5 so check for presence instead. - Object.prototype.hasOwnProperty.call(arr2, 'callee')) { - arr1.push.apply(arr1, arr2); - } else if (isArrayLike) { - // Otherwise loop over arr2 to prevent copying the object. - var len1 = arr1.length; - var len2 = arr2.length; + if (goog.isArrayLike(arr2)) { + var len1 = arr1.length || 0; + var len2 = arr2.length || 0; + arr1.length = len1 + len2; for (var j = 0; j < len2; j++) { arr1[len1 + j] = arr2[j]; } @@ -822,15 +890,16 @@ goog.array.extend = function(arr1, var_args) { * splice. This means that it might work on other objects similar to arrays, * such as the arguments object. * - * @param {goog.array.ArrayLike} arr The array to modify. + * @param {Array|goog.array.ArrayLike} arr The array to modify. * @param {number|undefined} index The index at which to start changing the * array. If not defined, treated as 0. * @param {number} howMany How many elements to remove (0 means no removal. A * value below 0 is treated as zero and so is any other non number. Numbers * are floored). - * @param {...*} var_args Optional, additional elements to insert into the + * @param {...T} var_args Optional, additional elements to insert into the * array. - * @return {!Array} the removed elements. + * @return {!Array} the removed elements. + * @template T */ goog.array.splice = function(arr, index, howMany, var_args) { goog.asserts.assert(arr.length != null); @@ -845,11 +914,11 @@ goog.array.splice = function(arr, index, howMany, var_args) { * Array slice. This means that it might work on other objects similar to * arrays, such as the arguments object. * - * @param {Array.|goog.array.ArrayLike} arr The array from + * @param {Array|goog.array.ArrayLike} arr The array from * which to copy a segment. * @param {number} start The index of the first element to copy. * @param {number=} opt_end The index after the last element to copy. - * @return {!Array.} A new array containing the specified segment of the + * @return {!Array} A new array containing the specified segment of the * original array. * @template T */ @@ -876,27 +945,36 @@ goog.array.slice = function(arr, start, opt_end) { * For objects, duplicates are identified as having the same unique ID as * defined by {@link 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) * - * @param {goog.array.ArrayLike} arr The array from which to remove duplicates. + * @param {Array|goog.array.ArrayLike} arr The array from which to remove + * duplicates. * @param {Array=} opt_rv An optional array in which to return the results, * instead of performing the removal inplace. If specified, the original * array will remain unchanged. + * @param {function(T):string=} opt_hashFn An optional function to use to + * apply to every item in the array. This function should return a unique + * value for each item in the array it should consider unique. + * @template T */ -goog.array.removeDuplicates = function(arr, opt_rv) { +goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) { var returnArray = opt_rv || arr; + var defaultHashFn = function(item) { + // Prefix each type with a single character representing the type to + // prevent conflicting keys (e.g. true and 'true'). + return goog.isObject(item) ? 'o' + goog.getUid(item) : + (typeof item).charAt(0) + item; + }; + var hashFn = opt_hashFn || defaultHashFn; var seen = {}, cursorInsert = 0, cursorRead = 0; while (cursorRead < arr.length) { var current = arr[cursorRead++]; - - // Prefix each type with a single character representing the type to - // prevent conflicting keys (e.g. true and 'true'). - var key = goog.isObject(current) ? - 'o' + goog.getUid(current) : - (typeof current).charAt(0) + current; - + var key = hashFn(current); if (!Object.prototype.hasOwnProperty.call(seen, key)) { seen[key] = true; returnArray[cursorInsert++] = current; @@ -919,16 +997,18 @@ goog.array.removeDuplicates = function(arr, opt_rv) { * * Runtime: O(log n) * - * @param {goog.array.ArrayLike} arr The array to be searched. - * @param {*} target The sought value. - * @param {Function=} opt_compareFn Optional comparison function by which the - * array is ordered. Should take 2 arguments to compare, and return a - * negative number, zero, or a positive number depending on whether the - * first argument is less than, equal to, or greater than the second. + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {TARGET} target The sought value. + * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. * @return {number} 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. + * @template TARGET, VALUE */ goog.array.binarySearch = function(arr, target, opt_compareFn) { return goog.array.binarySearch_(arr, @@ -946,18 +1026,20 @@ goog.array.binarySearch = function(arr, target, opt_compareFn) { * * Runtime: O(log n) * - * @param {goog.array.ArrayLike} arr The array to be searched. - * @param {Function} evaluator Evaluator function that receives 3 arguments - * (the element, the index and the array). Should return a negative number, - * zero, or a positive number depending on whether the desired index is - * before, at, or after the element passed to it. - * @param {Object=} opt_obj The object to be used as the value of 'this' + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {function(this:THIS, VALUE, number, ?): number} evaluator + * Evaluator function that receives 3 arguments (the element, the index and + * the array). Should return a negative number, zero, or a positive number + * depending on whether the desired index is before, at, or after the + * element passed to it. + * @param {THIS=} opt_obj The object to be used as the value of 'this' * within evaluator. * @return {number} 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. + * @template THIS, VALUE */ goog.array.binarySelect = function(arr, evaluator, opt_obj) { return goog.array.binarySearch_(arr, evaluator, true /* isEvaluator */, @@ -977,15 +1059,16 @@ goog.array.binarySelect = function(arr, evaluator, opt_obj) { * * Runtime: O(log n) * - * @param {goog.array.ArrayLike} arr The array to be searched. - * @param {Function} compareFn Either an evaluator or a comparison function, - * as defined by binarySearch and binarySelect above. + * @param {Array|goog.array.ArrayLike} arr The array to be searched. + * @param {function(?, ?, ?): number | function(?, ?): number} compareFn + * Either an evaluator or a comparison function, as defined by binarySearch + * and binarySelect above. * @param {boolean} isEvaluator Whether the function is an evaluator or a * comparison function. - * @param {*=} opt_target If the function is a comparison function, then this is - * the target to binary search for. + * @param {?=} opt_target If the function is a comparison function, then + * this is the target to binary search for. * @param {Object=} opt_selfObj If the function is an evaluator, this is an - * optional this object for the evaluator. + * optional this object for the evaluator. * @return {number} 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 @@ -1032,7 +1115,7 @@ goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target, * * Runtime: Same as Array.prototype.sort * - * @param {Array.} arr The array to be sorted. + * @param {Array} arr The array to be sorted. * @param {?function(T,T):number=} opt_compareFn Optional comparison * function by which the * array is to be ordered. Should take 2 arguments to compare, and return a @@ -1042,10 +1125,7 @@ goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target, */ goog.array.sort = function(arr, opt_compareFn) { // TODO(arv): Update type annotation since null is not accepted. - goog.asserts.assert(arr.length != null); - - goog.array.ARRAY_PROTOTYPE_.sort.call( - arr, opt_compareFn || goog.array.defaultCompare); + arr.sort(opt_compareFn || goog.array.defaultCompare); }; @@ -1059,7 +1139,7 @@ goog.array.sort = function(arr, opt_compareFn) { * Runtime: Same as Array.prototype.sort, plus an additional * O(n) overhead of copying the array twice. * - * @param {Array.} arr The array to be sorted. + * @param {Array} arr The array to be sorted. * @param {?function(T, T): number=} opt_compareFn Optional comparison function * by which the array is to be ordered. Should take 2 arguments to compare, * and return a negative number, zero, or a positive number depending on @@ -1082,28 +1162,54 @@ goog.array.stableSort = function(arr, opt_compareFn) { }; +/** + * Sort the specified array into ascending order based on item keys + * returned by the specified key function. + * If no opt_compareFn is specified, the keys are compared in ascending order + * using goog.array.defaultCompare. + * + * Runtime: O(S(f(n)), where S is runtime of goog.array.sort + * and f(n) is runtime of the key function. + * + * @param {Array} arr The array to be sorted. + * @param {function(T): K} keyFn Function taking array element and returning + * a key used for sorting this element. + * @param {?function(K, K): number=} opt_compareFn Optional comparison function + * by which the keys are to be ordered. Should take 2 arguments to compare, + * and return a negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template T,K + */ +goog.array.sortByKey = function(arr, keyFn, opt_compareFn) { + var keyCompareFn = opt_compareFn || goog.array.defaultCompare; + goog.array.sort(arr, function(a, b) { + return keyCompareFn(keyFn(a), keyFn(b)); + }); +}; + + /** * 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 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.} arr An array of objects to sort. + * @param {Array} arr An array of objects to sort. * @param {string} key The object key to sort by. * @param {Function=} opt_compareFn The function to use to compare key * values. */ goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) { - var compare = opt_compareFn || goog.array.defaultCompare; - goog.array.sort(arr, function(a, b) { - return compare(a[key], b[key]); - }); + goog.array.sortByKey(arr, + function(obj) { return obj[key]; }, + opt_compareFn); }; /** * Tells if the array is sorted. - * @param {!Array.} arr The array. + * @param {!Array} arr The array. * @param {?function(T,T):number=} opt_compareFn Function to compare the * array elements. * Should take 2 arguments to compare, and return a negative number, zero, @@ -1154,30 +1260,21 @@ goog.array.equals = function(arr1, arr2, opt_equalsFn) { }; -/** - * @deprecated Use {@link goog.array.equals}. - * @param {goog.array.ArrayLike} arr1 See {@link goog.array.equals}. - * @param {goog.array.ArrayLike} arr2 See {@link goog.array.equals}. - * @param {Function=} opt_equalsFn See {@link goog.array.equals}. - * @return {boolean} See {@link goog.array.equals}. - */ -goog.array.compare = function(arr1, arr2, opt_equalsFn) { - return goog.array.equals(arr1, arr2, opt_equalsFn); -}; - - /** * 3-way array compare function. - * @param {!goog.array.ArrayLike} arr1 The first array to compare. - * @param {!goog.array.ArrayLike} arr2 The second array to compare. - * @param {?function(?, ?): number=} opt_compareFn Optional comparison function - * by which the array is to be ordered. Should take 2 arguments to compare, - * and return a negative number, zero, or a positive number depending on - * whether the first argument is less than, equal to, or greater than the - * second. + * @param {!Array|!goog.array.ArrayLike} arr1 The first array to + * compare. + * @param {!Array|!goog.array.ArrayLike} arr2 The second array to + * compare. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is to be ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. * @return {number} Negative number, zero, or a positive number depending on * whether the first argument is less than, equal to, or greater than the * second. + * @template VALUE */ goog.array.compare3 = function(arr1, arr2, opt_compareFn) { var compare = opt_compareFn || goog.array.defaultCompare; @@ -1195,16 +1292,33 @@ goog.array.compare3 = function(arr1, arr2, opt_compareFn) { /** * Compares its two arguments for order, using the built in < and > * operators. - * @param {*} a The first object to be compared. - * @param {*} b The second object to be compared. + * @param {VALUE} a The first object to be compared. + * @param {VALUE} b The second object to be compared. * @return {number} A negative number, zero, or a positive number as the first - * argument is less than, equal to, or greater than the second. + * argument is less than, equal to, or greater than the second, + * respectively. + * @template VALUE */ goog.array.defaultCompare = function(a, b) { return a > b ? 1 : a < b ? -1 : 0; }; +/** + * Compares its two arguments for inverse order, using the built in < and > + * operators. + * @param {VALUE} a The first object to be compared. + * @param {VALUE} b The second object to be compared. + * @return {number} A negative number, zero, or a positive number as the first + * argument is greater than, equal to, or less than the second, + * respectively. + * @template VALUE + */ +goog.array.inverseDefaultCompare = function(a, b) { + return -goog.array.defaultCompare(a, b); +}; + + /** * Compares its two arguments for equality, using the built in === operator. * @param {*} a The first object to compare. @@ -1219,15 +1333,15 @@ goog.array.defaultCompareEquality = function(a, b) { /** * Inserts a value into a sorted array. The array is not modified if the * value is already present. - * @param {Array.} array The array to modify. - * @param {T} value The object to insert. - * @param {?function(T,T):number=} opt_compareFn Optional comparison function by - * which the - * array is ordered. Should take 2 arguments to compare, and return a - * negative number, zero, or a positive number depending on whether the - * first argument is less than, equal to, or greater than the second. + * @param {Array|goog.array.ArrayLike} array The array to modify. + * @param {VALUE} value The object to insert. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. * @return {boolean} True if an element was inserted. - * @template T + * @template VALUE */ goog.array.binaryInsert = function(array, value, opt_compareFn) { var index = goog.array.binarySearch(array, value, opt_compareFn); @@ -1241,13 +1355,15 @@ goog.array.binaryInsert = function(array, value, opt_compareFn) { /** * Removes a value from a sorted array. - * @param {Array} array The array to modify. - * @param {*} value The object to remove. - * @param {Function=} opt_compareFn Optional comparison function by which the - * array is ordered. Should take 2 arguments to compare, and return a - * negative number, zero, or a positive number depending on whether the - * first argument is less than, equal to, or greater than the second. + * @param {!Array|!goog.array.ArrayLike} array The array to modify. + * @param {VALUE} value The object to remove. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. * @return {boolean} True if an element was removed. + * @template VALUE */ goog.array.binaryRemove = function(array, value, opt_compareFn) { var index = goog.array.binarySearch(array, value, opt_compareFn); @@ -1257,8 +1373,8 @@ goog.array.binaryRemove = function(array, value, opt_compareFn) { /** * Splits an array into disjoint buckets according to a splitting function. - * @param {Array.} array The array. - * @param {function(this:S, T,number,Array.):?} sorter Function to call for + * @param {Array} array The array. + * @param {function(this:S, T,number,Array):?} sorter Function to call for * every element. This takes 3 arguments (the element, the index and the * array) and must return a valid object key (a string, number, etc), or * undefined, if that object should not be placed in a bucket. @@ -1289,7 +1405,7 @@ goog.array.bucket = function(array, sorter, opt_obj) { /** * Creates a new object built from the provided array and the key-generation * function. - * @param {Array.|goog.array.ArrayLike} arr Array or array like object over + * @param {Array|goog.array.ArrayLike} arr Array or array like object over * which to iterate whose elements will be the values in the new object. * @param {?function(this:S, T, number, ?) : string} keyFunc The function to * call for every element. This function takes 3 arguments (the element, the @@ -1299,7 +1415,7 @@ goog.array.bucket = function(array, sorter, opt_obj) { * implementation-defined. * @param {S=} opt_obj The object to be used as the value of 'this' * within keyFunc. - * @return {!Object.} The new object. + * @return {!Object} The new object. * @template T,S */ goog.array.toObject = function(arr, keyFunc, opt_obj) { @@ -1327,7 +1443,7 @@ goog.array.toObject = function(arr, keyFunc, opt_obj) { * @param {number=} opt_end The optional end value of the range. * @param {number=} opt_step The step size between range values. Defaults to 1 * if opt_step is undefined or 0. - * @return {!Array.} An array of numbers for the requested range. May be + * @return {!Array} An array of numbers for the requested range. May be * an empty array if adding the step would not converge toward the end * value. */ @@ -1362,9 +1478,10 @@ goog.array.range = function(startOrEnd, opt_end, opt_step) { /** * Returns an array consisting of the given value repeated N times. * - * @param {*} value The value to repeat. + * @param {VALUE} value The value to repeat. * @param {number} n The repeat count. - * @return {!Array} An array with the repeated value. + * @return {!Array} An array with the repeated value. + * @template VALUE */ goog.array.repeat = function(value, n) { var array = []; @@ -1380,14 +1497,22 @@ goog.array.repeat = function(value, n) { * expanded in-place recursively. * * @param {...*} var_args The values to flatten. - * @return {!Array} An array containing the flattened values. + * @return {!Array} An array containing the flattened values. */ goog.array.flatten = function(var_args) { + var CHUNK_SIZE = 8192; + var result = []; for (var i = 0; i < arguments.length; i++) { var element = arguments[i]; if (goog.isArray(element)) { - result.push.apply(result, goog.array.flatten.apply(null, element)); + for (var c = 0; c < element.length; c += CHUNK_SIZE) { + var chunk = goog.array.slice(element, c, c + CHUNK_SIZE); + var recurseResult = goog.array.flatten.apply(null, chunk); + for (var r = 0; r < recurseResult.length; r++) { + result.push(recurseResult[r]); + } + } } else { result.push(element); } @@ -1405,9 +1530,9 @@ goog.array.flatten = function(var_args) { * 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]. * - * @param {!Array.} array The array to rotate. + * @param {!Array} array The array to rotate. * @param {number} n The amount to rotate. - * @return {!Array.} The array. + * @return {!Array} The array. * @template T */ goog.array.rotate = function(array, n) { @@ -1425,6 +1550,28 @@ goog.array.rotate = function(array, n) { }; +/** + * 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. + * @param {!(Array|Arguments|{length:number})} arr The array to modify. + * @param {number} fromIndex Index of the item to move between 0 and + * {@code arr.length - 1}. + * @param {number} toIndex Target index between 0 and {@code arr.length - 1}. + */ +goog.array.moveItem = function(arr, fromIndex, toIndex) { + goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length); + goog.asserts.assert(toIndex >= 0 && toIndex < arr.length); + // Remove 1 item at fromIndex. + var removedItems = goog.array.ARRAY_PROTOTYPE_.splice.call(arr, fromIndex, 1); + // Insert the removed item at toIndex. + goog.array.ARRAY_PROTOTYPE_.splice.call(arr, toIndex, 0, removedItems[0]); + // We don't use goog.array.insertAt and goog.array.removeAt, because they're + // significantly slower than splice. +}; + + /** * 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 @@ -1435,25 +1582,28 @@ goog.array.rotate = function(array, n) { * http://docs.python.org/library/functions.html#zip} * * @param {...!goog.array.ArrayLike} var_args Arrays to be combined. - * @return {!Array.} A new array of arrays created from provided arrays. + * @return {!Array>} A new array of arrays created from + * provided arrays. */ goog.array.zip = function(var_args) { if (!arguments.length) { return []; } var result = []; - for (var i = 0; true; i++) { + var minLen = arguments[0].length; + for (var i = 1; i < arguments.length; i++) { + if (arguments[i].length < minLen) { + minLen = arguments[i].length; + } + } + for (var i = 0; i < minLen; i++) { var value = []; for (var j = 0; j < arguments.length; j++) { - var arr = arguments[j]; - // If i is larger than the array length, this is the shortest array. - if (i >= arr.length) { - return result; - } - value.push(arr[i]); + value.push(arguments[j][i]); } result.push(value); } + return result; }; @@ -1465,7 +1615,7 @@ goog.array.zip = function(var_args) { * * Runtime: O(n) * - * @param {!Array} arr The array to be shuffled. + * @param {!Array} arr The array to be shuffled. * @param {function():number=} opt_randFn Optional random function to use for * shuffling. * Takes no arguments, and returns a random number on the interval [0, 1). @@ -1483,3 +1633,22 @@ goog.array.shuffle = function(arr, opt_randFn) { arr[j] = tmp; } }; + + +/** + * Returns a new array of elements from arr, based on the indexes of elements + * provided by index_arr. For example, the result of index copying + * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c']. + * + * @param {!Array} arr The array to get a indexed copy from. + * @param {!Array} index_arr An array of indexes to get from arr. + * @return {!Array} A new array of elements from arr in index_arr order. + * @template T + */ +goog.array.copyByIndex = function(arr, index_arr) { + var result = []; + goog.array.forEach(index_arr, function(index) { + result.push(arr[index]); + }); + return result; +}; diff --git a/lib/goog/asserts/asserts.js b/lib/goog/asserts/asserts.js index 905cd58..8522e7c 100644 --- a/lib/goog/asserts/asserts.js +++ b/lib/goog/asserts/asserts.js @@ -31,12 +31,14 @@ * The compiler will leave in foo() (because its return value is used), * but it will remove bar() because it assumes it does not have side-effects. * + * @author agrieve@google.com (Andrew Grieve) */ goog.provide('goog.asserts'); goog.provide('goog.asserts.AssertionError'); goog.require('goog.debug.Error'); +goog.require('goog.dom.NodeType'); goog.require('goog.string'); @@ -50,14 +52,15 @@ goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG); /** * Error object for failed assertions. * @param {string} messagePattern The pattern that was used to form message. - * @param {!Array.<*>} messageArgs The items to substitute into the pattern. + * @param {!Array<*>} messageArgs The items to substitute into the pattern. * @constructor * @extends {goog.debug.Error} + * @final */ goog.asserts.AssertionError = function(messagePattern, messageArgs) { messageArgs.unshift(messagePattern); goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs)); - // Remove the messagePattern afterwards to avoid permenantly modifying the + // Remove the messagePattern afterwards to avoid permanently modifying the // passed in array. messageArgs.shift(); @@ -75,13 +78,27 @@ goog.inherits(goog.asserts.AssertionError, goog.debug.Error); goog.asserts.AssertionError.prototype.name = 'AssertionError'; +/** + * The default error handler. + * @param {!goog.asserts.AssertionError} e The exception to be handled. + */ +goog.asserts.DEFAULT_ERROR_HANDLER = function(e) { throw e; }; + + +/** + * The handler responsible for throwing or logging assertion errors. + * @private {function(!goog.asserts.AssertionError)} + */ +goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER; + + /** * Throws an exception with the given message and "Assertion failed" prefixed * onto it. * @param {string} defaultMessage The message to use if givenMessage is empty. - * @param {Array.<*>} defaultArgs The substitution arguments for defaultMessage. + * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage. * @param {string|undefined} givenMessage Message supplied by the caller. - * @param {Array.<*>} givenArgs The substitution arguments for givenMessage. + * @param {Array<*>} givenArgs The substitution arguments for givenMessage. * @throws {goog.asserts.AssertionError} When the value is not a number. * @private */ @@ -99,17 +116,32 @@ goog.asserts.doAssertFailure_ = // a stack trace is added to var message above. With this, a stack trace is // not added until this line (it causes the extra garbage to be added after // the assertion message instead of in the middle of it). - throw new goog.asserts.AssertionError('' + message, args || []); + var e = new goog.asserts.AssertionError('' + message, args || []); + goog.asserts.errorHandler_(e); +}; + + +/** + * Sets a custom error handler that can be used to customize the behavior of + * assertion failures, for example by turning all assertion failures into log + * messages. + * @param {function(!goog.asserts.AssertionError)} errorHandler + */ +goog.asserts.setErrorHandler = function(errorHandler) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.errorHandler_ = errorHandler; + } }; /** * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is * true. - * @param {*} condition The condition to check. + * @template T + * @param {T} condition The condition to check. * @param {string=} opt_message Error message in case of failure. * @param {...*} var_args The items to substitute into the failure message. - * @return {*} The value of the condition. + * @return {T} The value of the condition. * @throws {goog.asserts.AssertionError} When the condition evaluates to false. */ goog.asserts.assert = function(condition, opt_message, var_args) { @@ -141,9 +173,9 @@ goog.asserts.assert = function(condition, opt_message, var_args) { */ goog.asserts.fail = function(opt_message, var_args) { if (goog.asserts.ENABLE_ASSERTS) { - throw new goog.asserts.AssertionError( + goog.asserts.errorHandler_(new goog.asserts.AssertionError( 'Failure' + (opt_message ? ': ' + opt_message : ''), - Array.prototype.slice.call(arguments, 1)); + Array.prototype.slice.call(arguments, 1))); } }; @@ -226,7 +258,7 @@ goog.asserts.assertObject = function(value, opt_message, var_args) { * @param {*} value The value to check. * @param {string=} opt_message Error message in case of failure. * @param {...*} var_args The items to substitute into the failure message. - * @return {!Array} The value, guaranteed to be a non-null array. + * @return {!Array} The value, guaranteed to be a non-null array. * @throws {goog.asserts.AssertionError} When the value is not an array. */ goog.asserts.assertArray = function(value, opt_message, var_args) { @@ -235,7 +267,7 @@ goog.asserts.assertArray = function(value, opt_message, var_args) { [goog.typeOf(value), value], opt_message, Array.prototype.slice.call(arguments, 2)); } - return /** @type {!Array} */ (value); + return /** @type {!Array} */ (value); }; @@ -258,26 +290,76 @@ goog.asserts.assertBoolean = function(value, opt_message, var_args) { }; +/** + * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Element} The value, likely to be a DOM Element when asserts are + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not an Element. + */ +goog.asserts.assertElement = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && (!goog.isObject(value) || + value.nodeType != goog.dom.NodeType.ELEMENT)) { + goog.asserts.doAssertFailure_('Expected Element but got %s: %s.', + [goog.typeOf(value), value], opt_message, + Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Element} */ (value); +}; + + /** * 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. * - * @param {*} value The value to check. + * @param {?} value The value to check. * @param {function(new: T, ...)} type A user-defined constructor. * @param {string=} opt_message Error message in case of failure. * @param {...*} var_args The items to substitute into the failure message. * @throws {goog.asserts.AssertionError} When the value is not an instance of * type. - * @return {!T} + * @return {T} * @template T */ goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) { if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) { - goog.asserts.doAssertFailure_('instanceof check failed.', null, + goog.asserts.doAssertFailure_('Expected instanceof %s but got %s.', + [goog.asserts.getType_(type), goog.asserts.getType_(value)], opt_message, Array.prototype.slice.call(arguments, 3)); } return value; }; + +/** + * Checks that no enumerable keys are present in Object.prototype. Such keys + * would break most code that use {@code for (var ... in ...)} loops. + */ +goog.asserts.assertObjectPrototypeIsIntact = function() { + for (var key in Object.prototype) { + goog.asserts.fail(key + ' should not be enumerable in Object.prototype.'); + } +}; + + +/** + * Returns the type of a value. If a constructor is passed, and a suitable + * string cannot be found, 'unknown type name' will be returned. + * @param {*} value A constructor, object, or primitive. + * @return {string} The best display name for the value, or 'unknown type name'. + * @private + */ +goog.asserts.getType_ = function(value) { + if (value instanceof Function) { + return value.displayName || value.name || 'unknown type name'; + } else if (value instanceof Object) { + return value.constructor.displayName || value.constructor.name || + Object.prototype.toString.call(value); + } else { + return value === null ? 'null' : typeof value; + } +}; diff --git a/lib/goog/async/freelist.js b/lib/goog/async/freelist.js new file mode 100644 index 0000000..d0331f2 --- /dev/null +++ b/lib/goog/async/freelist.js @@ -0,0 +1,88 @@ +// Copyright 2015 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 Simple freelist. + * + * An anterative to goog.structs.SimplePool, it imposes the requirement that the + * objects in the list contain a "next" property that can be used to maintain + * the pool. + */ + +goog.provide('goog.async.FreeList'); + + +/** + * @template ITEM + */ +goog.async.FreeList = goog.defineClass(null, { + /** + * @param {function():ITEM} create + * @param {function(ITEM):void} reset + * @param {number} limit + */ + constructor: function(create, reset, limit) { + /** @const {number} */ + this.limit_ = limit; + /** @const {function()} */ + this.create_ = create; + /** @const {function(ITEM):void} */ + this.reset_ = reset; + + /** @type {number} */ + this.occupants_ = 0; + /** @type {ITEM} */ + this.head_ = null; + }, + + /** + * @return {ITEM} + */ + get: function() { + var item; + if (this.occupants_ > 0) { + this.occupants_--; + item = this.head_; + this.head_ = item.next; + item.next = null; + } else { + item = this.create_(); + } + return item; + }, + + /** + * @param {ITEM} item An item available for possible future reuse. + */ + put: function(item) { + this.reset_(item); + if (this.occupants_ < this.limit_) { + this.occupants_++; + item.next = this.head_; + this.head_ = item; + } + }, + + /** + * Visible for testing. + * @package + * @return {number} + */ + occupants: function() { + return this.occupants_; + } +}); + + + diff --git a/lib/goog/async/nexttick.js b/lib/goog/async/nexttick.js new file mode 100644 index 0000000..bdbad6b --- /dev/null +++ b/lib/goog/async/nexttick.js @@ -0,0 +1,241 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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 Provides a function to schedule running a function as soon + * as possible after the current JS execution stops and yields to the event + * loop. + * + */ + +goog.provide('goog.async.nextTick'); +goog.provide('goog.async.throwException'); + +goog.require('goog.debug.entryPointRegistry'); +goog.require('goog.dom.TagName'); +goog.require('goog.functions'); +goog.require('goog.labs.userAgent.browser'); +goog.require('goog.labs.userAgent.engine'); + + +/** + * Throw an item without interrupting the current execution context. For + * example, if processing a group of items in a loop, sometimes it is useful + * to report an error while still allowing the rest of the batch to be + * processed. + * @param {*} exception + */ +goog.async.throwException = function(exception) { + // Each throw needs to be in its own context. + goog.global.setTimeout(function() { throw exception; }, 0); +}; + + +/** + * Fires the provided callbacks as soon as possible after the current JS + * execution context. setTimeout(…, 0) takes at least 4ms when called from + * within another setTimeout(…, 0) for legacy reasons. + * + * This will not schedule the callback as a microtask (i.e. a task that can + * preempt user input or networking callbacks). It is meant to emulate what + * setTimeout(_, 0) would do if it were not throttled. If you desire microtask + * behavior, use {@see goog.Promise} instead. + * + * @param {function(this:SCOPE)} callback Callback function to fire as soon as + * possible. + * @param {SCOPE=} opt_context Object in whose scope to call the listener. + * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that + * ensures correctness at the cost of speed. See comments for details. + * @template SCOPE + */ +goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) { + var cb = callback; + if (opt_context) { + cb = goog.bind(callback, opt_context); + } + cb = goog.async.nextTick.wrapCallback_(cb); + // window.setImmediate was introduced and currently only supported by IE10+, + // but due to a bug in the implementation it is not guaranteed that + // setImmediate is faster than setTimeout nor that setImmediate N is before + // setImmediate N+1. That is why we do not use the native version if + // available. We do, however, call setImmediate if it is a normal function + // because that indicates that it has been replaced by goog.testing.MockClock + // which we do want to support. + // See + // http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10 + // + // Note we do allow callers to also request setImmediate if they are willing + // to accept the possible tradeoffs of incorrectness in exchange for speed. + // The IE fallback of readystate change is much slower. + if (goog.isFunction(goog.global.setImmediate) && + // Opt in. + (opt_useSetImmediate || + // or it isn't a browser or the environment is weird + !goog.global.Window || !goog.global.Window.prototype || + // or something redefined setImmediate in which case we (YOLO) decide + // to use it (This is so that we use the mockClock setImmediate. sigh). + goog.global.Window.prototype.setImmediate != goog.global.setImmediate)) { + goog.global.setImmediate(cb); + return; + } + + // Look for and cache the custom fallback version of setImmediate. + if (!goog.async.nextTick.setImmediate_) { + goog.async.nextTick.setImmediate_ = + goog.async.nextTick.getSetImmediateEmulator_(); + } + goog.async.nextTick.setImmediate_(cb); +}; + + +/** + * Cache for the setImmediate implementation. + * @type {function(function())} + * @private + */ +goog.async.nextTick.setImmediate_; + + +/** + * Determines the best possible implementation to run a function as soon as + * the JS event loop is idle. + * @return {function(function())} The "setImmediate" implementation. + * @private + */ +goog.async.nextTick.getSetImmediateEmulator_ = function() { + // Create a private message channel and use it to postMessage empty messages + // to ourselves. + var Channel = goog.global['MessageChannel']; + // If MessageChannel is not available and we are in a browser, implement + // an iframe based polyfill in browsers that have postMessage and + // document.addEventListener. The latter excludes IE8 because it has a + // synchronous postMessage implementation. + if (typeof Channel === 'undefined' && typeof window !== 'undefined' && + window.postMessage && window.addEventListener && + // Presto (The old pre-blink Opera engine) has problems with iframes + // and contentWindow. + !goog.labs.userAgent.engine.isPresto()) { + /** @constructor */ + Channel = function() { + // Make an empty, invisible iframe. + var iframe = document.createElement(goog.dom.TagName.IFRAME); + iframe.style.display = 'none'; + iframe.src = ''; + document.documentElement.appendChild(iframe); + var win = iframe.contentWindow; + var doc = win.document; + doc.open(); + doc.write(''); + doc.close(); + // Do not post anything sensitive over this channel, as the workaround for + // pages with file: origin could allow that information to be modified or + // intercepted. + var message = 'callImmediate' + Math.random(); + // The same origin policy rejects attempts to postMessage from file: urls + // unless the origin is '*'. + // TODO(b/16335441): Use '*' origin for data: and other similar protocols. + var origin = win.location.protocol == 'file:' ? + '*' : win.location.protocol + '//' + win.location.host; + var onmessage = goog.bind(function(e) { + // Validate origin and message to make sure that this message was + // intended for us. If the origin is set to '*' (see above) only the + // message needs to match since, for example, '*' != 'file://'. Allowing + // the wildcard is ok, as we are not concerned with security here. + if ((origin != '*' && e.origin != origin) || e.data != message) { + return; + } + this['port1'].onmessage(); + }, this); + win.addEventListener('message', onmessage, false); + this['port1'] = {}; + this['port2'] = { + postMessage: function() { + win.postMessage(message, origin); + } + }; + }; + } + if (typeof Channel !== 'undefined' && + (!goog.labs.userAgent.browser.isIE())) { + // Exclude all of IE due to + // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ + // which allows starving postMessage with a busy setTimeout loop. + // This currently affects IE10 and IE11 which would otherwise be able + // to use the postMessage based fallbacks. + var channel = new Channel(); + // Use a fifo linked list to call callbacks in the right order. + var head = {}; + var tail = head; + channel['port1'].onmessage = function() { + if (goog.isDef(head.next)) { + head = head.next; + var cb = head.cb; + head.cb = null; + cb(); + } + }; + return function(cb) { + tail.next = { + cb: cb + }; + tail = tail.next; + channel['port2'].postMessage(0); + }; + } + // Implementation for IE6+: Script elements fire an asynchronous + // onreadystatechange event when inserted into the DOM. + if (typeof document !== 'undefined' && 'onreadystatechange' in + document.createElement(goog.dom.TagName.SCRIPT)) { + return function(cb) { + var script = document.createElement(goog.dom.TagName.SCRIPT); + script.onreadystatechange = function() { + // Clean up and call the callback. + script.onreadystatechange = null; + script.parentNode.removeChild(script); + script = null; + cb(); + cb = null; + }; + document.documentElement.appendChild(script); + }; + } + // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms + // or more. + return function(cb) { + goog.global.setTimeout(cb, 0); + }; +}; + + +/** + * Helper function that is overrided to protect callbacks with entry point + * monitor if the application monitors entry points. + * @param {function()} callback Callback function to fire as soon as possible. + * @return {function()} The wrapped callback. + * @private + */ +goog.async.nextTick.wrapCallback_ = goog.functions.identity; + + +// Register the callback function as an entry point, so that it can be +// monitored for exception handling, etc. This has to be done in this file +// since it requires special code to handle all browsers. +goog.debug.entryPointRegistry.register( + /** + * @param {function(!Function): !Function} transformer The transforming + * function. + */ + function(transformer) { + goog.async.nextTick.wrapCallback_ = transformer; + }); diff --git a/lib/goog/async/run.js b/lib/goog/async/run.js new file mode 100644 index 0000000..749c9b9 --- /dev/null +++ b/lib/goog/async/run.js @@ -0,0 +1,133 @@ +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// 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. + +goog.provide('goog.async.run'); + +goog.require('goog.async.WorkQueue'); +goog.require('goog.async.nextTick'); +goog.require('goog.async.throwException'); + + +/** + * Fires the provided callback just before the current callstack unwinds, or as + * soon as possible after the current JS execution context. + * @param {function(this:THIS)} callback + * @param {THIS=} opt_context Object to use as the "this value" when calling + * the provided function. + * @template THIS + */ +goog.async.run = function(callback, opt_context) { + if (!goog.async.run.schedule_) { + goog.async.run.initializeRunner_(); + } + if (!goog.async.run.workQueueScheduled_) { + // Nothing is currently scheduled, schedule it now. + goog.async.run.schedule_(); + goog.async.run.workQueueScheduled_ = true; + } + + goog.async.run.workQueue_.add(callback, opt_context); +}; + + +/** + * Initializes the function to use to process the work queue. + * @private + */ +goog.async.run.initializeRunner_ = function() { + // If native Promises are available in the browser, just schedule the callback + // on a fulfilled promise, which is specified to be async, but as fast as + // possible. + if (goog.global.Promise && goog.global.Promise.resolve) { + var promise = goog.global.Promise.resolve(undefined); + goog.async.run.schedule_ = function() { + promise.then(goog.async.run.processWorkQueue); + }; + } else { + goog.async.run.schedule_ = function() { + goog.async.nextTick(goog.async.run.processWorkQueue); + }; + } +}; + + +/** + * Forces goog.async.run to use nextTick instead of Promise. + * + * This should only be done in unit tests. It's useful because MockClock + * replaces nextTick, but not the browser Promise implementation, so it allows + * Promise-based code to be tested with MockClock. + * + * However, we also want to run promises if the MockClock is no longer in + * control so we schedule a backup "setTimeout" to the unmocked timeout if + * provided. + * + * @param {function(function())=} opt_realSetTimeout + */ +goog.async.run.forceNextTick = function(opt_realSetTimeout) { + goog.async.run.schedule_ = function() { + goog.async.nextTick(goog.async.run.processWorkQueue); + if (opt_realSetTimeout) { + opt_realSetTimeout(goog.async.run.processWorkQueue); + } + }; +}; + + +/** + * The function used to schedule work asynchronousely. + * @private {function()} + */ +goog.async.run.schedule_; + + +/** @private {boolean} */ +goog.async.run.workQueueScheduled_ = false; + + +/** @private {!goog.async.WorkQueue} */ +goog.async.run.workQueue_ = new goog.async.WorkQueue(); + + +if (goog.DEBUG) { + /** + * Reset the work queue. Only available for tests in debug mode. + */ + goog.async.run.resetQueue = function() { + goog.async.run.workQueueScheduled_ = false; + goog.async.run.workQueue_ = new goog.async.WorkQueue(); + }; +} + + +/** + * Run any pending goog.async.run work items. This function is not intended + * for general use, but for use by entry point handlers to run items ahead of + * goog.async.nextTick. + */ +goog.async.run.processWorkQueue = function() { + // NOTE: additional work queue items may be added while processing. + var item = null; + while (item = goog.async.run.workQueue_.remove()) { + try { + item.fn.call(item.scope); + } catch (e) { + goog.async.throwException(e); + } + goog.async.run.workQueue_.returnUnused(item); + } + + // There are no more work items, allow processing to be scheduled again. + goog.async.run.workQueueScheduled_ = false; +}; diff --git a/lib/goog/async/workqueue.js b/lib/goog/async/workqueue.js new file mode 100644 index 0000000..2d86c89 --- /dev/null +++ b/lib/goog/async/workqueue.js @@ -0,0 +1,139 @@ +// Copyright 2015 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. + +goog.provide('goog.async.WorkItem'); +goog.provide('goog.async.WorkQueue'); + +goog.require('goog.asserts'); +goog.require('goog.async.FreeList'); + + +// TODO(johnlenz): generalize the WorkQueue if this is used by more +// than goog.async.run. + + + +/** + * A low GC workqueue. The key elements of this design: + * - avoids the need for goog.bind or equivalent by carrying scope + * - avoids the need for array reallocation by using a linked list + * - minimizes work entry objects allocation by recycling objects + * @constructor + * @final + * @struct + */ +goog.async.WorkQueue = function() { + this.workHead_ = null; + this.workTail_ = null; +}; + + +/** @define {number} The maximum number of entries to keep for recycling. */ +goog.define('goog.async.WorkQueue.DEFAULT_MAX_UNUSED', 100); + + +/** @const @private {goog.async.FreeList} */ +goog.async.WorkQueue.freelist_ = new goog.async.FreeList( + function() {return new goog.async.WorkItem(); }, + function(item) {item.reset()}, + goog.async.WorkQueue.DEFAULT_MAX_UNUSED); + + +/** + * @param {function()} fn + * @param {Object|null|undefined} scope + */ +goog.async.WorkQueue.prototype.add = function(fn, scope) { + var item = this.getUnusedItem_(); + item.set(fn, scope); + + if (this.workTail_) { + this.workTail_.next = item; + this.workTail_ = item; + } else { + goog.asserts.assert(!this.workHead_); + this.workHead_ = item; + this.workTail_ = item; + } +}; + + +/** + * @return {goog.async.WorkItem} + */ +goog.async.WorkQueue.prototype.remove = function() { + var item = null; + + if (this.workHead_) { + item = this.workHead_; + this.workHead_ = this.workHead_.next; + if (!this.workHead_) { + this.workTail_ = null; + } + item.next = null; + } + return item; +}; + + +/** + * @param {goog.async.WorkItem} item + */ +goog.async.WorkQueue.prototype.returnUnused = function(item) { + goog.async.WorkQueue.freelist_.put(item); +}; + + +/** + * @return {goog.async.WorkItem} + * @private + */ +goog.async.WorkQueue.prototype.getUnusedItem_ = function() { + return goog.async.WorkQueue.freelist_.get(); +}; + + + +/** + * @constructor + * @final + * @struct + */ +goog.async.WorkItem = function() { + /** @type {?function()} */ + this.fn = null; + /** @type {Object|null|undefined} */ + this.scope = null; + /** @type {?goog.async.WorkItem} */ + this.next = null; +}; + + +/** + * @param {function()} fn + * @param {Object|null|undefined} scope + */ +goog.async.WorkItem.prototype.set = function(fn, scope) { + this.fn = fn; + this.scope = scope; + this.next = null; +}; + + +/** Reset the work item so they don't prevent GC before reuse */ +goog.async.WorkItem.prototype.reset = function() { + this.fn = null; + this.scope = null; + this.next = null; +}; diff --git a/lib/goog/base.js b/lib/goog/base.js index 39bd73a..39db84d 100644 --- a/lib/goog/base.js +++ b/lib/goog/base.js @@ -19,22 +19,23 @@ * global CLOSURE_NO_DEPS is set to true. This allows projects to * include their own deps file(s) from different locations. * + * @author arv@google.com (Erik Arvidsson) * * @provideGoog */ /** - * @define {boolean} Overridden to true by the compiler when --closure_pass - * or --mark_as_compiled is specified. + * @define {boolean} Overridden to true by the compiler when + * --process_closure_primitives is specified. */ var COMPILED = false; /** - * 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. + * 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. * * @const */ @@ -50,26 +51,61 @@ goog.global = this; /** * A hook for overriding the define values in uncompiled mode. * - * In uncompiled mode, {@code CLOSURE_DEFINES} may be defined before loading - * base.js. If a key is defined in {@code CLOSURE_DEFINES}, {@code goog.define} - * will use the value instead of the default value. This allows flags to be - * overwritten without compilation (this is normally accomplished with the - * compiler's "define" flag). + * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before + * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES}, + * {@code goog.define} will use the value instead of the default value. This + * allows flags to be overwritten without compilation (this is normally + * accomplished with the compiler's "define" flag). * * Example: *
        - *   var CLOSURE_DEFINES = {'goog.DEBUG', false};
        + *   var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
          * 
        * - * @type {Object.|undefined} + * @type {Object|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + *
        + *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
        + * 
        + * + * @type {Object|undefined} */ goog.global.CLOSURE_DEFINES; /** - * Builds an object structure for the provided namespace path, - * ensuring that names that already exist are not overwritten. For - * example: + * 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. + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined. + */ +goog.isDef = function(val) { + // void 0 always evaluates to undefined and hence we do not need to depend on + // the definition of the global variable named 'undefined'. + return val !== void 0; +}; + + +/** + * 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. * @param {string} name name of the object that this file defines. @@ -96,7 +132,7 @@ goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { // Parentheses added to eliminate strict JS warning in Firefox. for (var part; parts.length && (part = parts.shift());) { - if (!parts.length && opt_object !== undefined) { + if (!parts.length && goog.isDef(opt_object)) { // last part and we have an object; use it cur[part] = opt_object; } else if (cur[part]) { @@ -109,10 +145,11 @@ goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { /** - * Defines a named value. In uncompiled mode, the value is retreived from - * CLOSURE_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. + * Defines a named value. In uncompiled mode, the value is retrieved 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 the compiler + * options or the value set in the CLOSURE_DEFINES object. * * @param {string} name The distinguished name to provide. * @param {string|number|boolean} defaultValue @@ -120,8 +157,13 @@ goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { goog.define = function(name, defaultValue) { var value = defaultValue; if (!COMPILED) { - if (goog.global.CLOSURE_DEFINES && Object.prototype.hasOwnProperty.call( - goog.global.CLOSURE_DEFINES, name)) { + if (goog.global.CLOSURE_UNCOMPILED_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { + value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; + } else if (goog.global.CLOSURE_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_DEFINES, name)) { value = goog.global.CLOSURE_DEFINES[name]; } } @@ -137,7 +179,7 @@ goog.define = function(name, defaultValue) { * because they are generally used for debugging purposes and it is difficult * for the JSCompiler to statically determine whether they are used. */ -goog.DEBUG = true; +goog.define('goog.DEBUG', true); /** @@ -169,7 +211,7 @@ goog.define('goog.LOCALE', 'en'); // default to en * external libraries like Prototype, Datejs, and JQuery and setting this flag * to false forces closure to use its own implementations when possible. * - * If your javascript can be loaded by a third party site and you are wary about + * If your JavaScript can be loaded by a third party site and you are wary about * relying on non-standard implementations, specify * "--define goog.TRUSTED_SITE=false" to the JSCompiler. */ @@ -177,24 +219,73 @@ goog.define('goog.TRUSTED_SITE', true); /** - * Creates object stubs for a namespace. The presence of one or more - * goog.provide() calls indicate that the file defines the given - * objects/namespaces. Build tools also scan for provide/require statements + * @define {boolean} Whether a project is expected to be running in strict mode. + * + * This define can be used to trigger alternate implementations compatible with + * running in EcmaScript Strict mode or warn about unavailable functionality. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode + * + */ +goog.define('goog.STRICT_MODE_COMPATIBLE', false); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * @define {boolean} Whether to use a Chrome app CSP-compliant method for + * loading scripts via goog.require. @see appendScriptSrcNode_. + */ +goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); + + +/** + * Defines a namespace in Closure. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * The presence of one or more goog.provide() calls in a file indicates + * that the file defines the given objects/namespaces. + * Provided symbols must not be null or undefined. + * + * In addition, goog.provide() creates the object stubs for a namespace + * (for example, goog.provide("goog.foo.bar") will create the object + * goog.foo.bar if it does not already exist). + * + * Build tools also scan for provide/require/module statements * to discern dependencies, build dependency files (see deps.js), etc. + * * @see goog.require + * @see goog.module * @param {string} name Namespace provided by this file in the form * "goog.package.part". */ goog.provide = function(name) { if (!COMPILED) { - // Ensure that the same namespace isn't provided twice. This is intended - // to teach new developers that 'goog.provide' is effectively a variable - // declaration. And when JSCompiler transforms goog.provide into a real - // variable declaration, the compiled JS should work the same as the raw - // JS--even when the raw JS uses goog.provide incorrectly. + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file if (goog.isProvided_(name)) { throw Error('Namespace "' + name + '" already declared.'); } + } + + goog.constructNamespace_(name); +}; + + +/** + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + * @param {Object=} opt_obj The object to embed in the namespace. + * @private + */ +goog.constructNamespace_ = function(name, opt_obj) { + if (!COMPILED) { delete goog.implicitNamespaces_[name]; var namespace = name; @@ -206,7 +297,142 @@ goog.provide = function(name) { } } - goog.exportPath_(name); + goog.exportPath_(name, opt_obj); +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lenient, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + */ +goog.module = function(name) { + if (!goog.isString(name) || + !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw Error('Invalid module identifier'); + } + if (!goog.isInModuleLoader_()) { + throw Error('Module ' + name + ' has been loaded incorrectly.'); + } + if (goog.moduleLoaderState_.moduleName) { + throw Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + return goog.module.getInternal_(name); +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (goog.isProvided_(name)) { + // goog.require only return a value with-in goog.module files. + return name in goog.loadedModules_ ? + goog.loadedModules_[name] : + goog.getObjectByName(name); + } else { + return null; + } + } +}; + + +/** + * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}} + */ +goog.moduleLoaderState_ = null; + + +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInModuleLoader_ = function() { + return goog.moduleLoaderState_ != null; +}; + + +/** + * Provide the module's exports as a globally accessible object under the + * module's declared name. This is intended to ease migration to goog.module + * for files that have existing usages. + * @suppress {missingProvide} + */ +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInModuleLoader_()) { + throw new Error('goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw Error('goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; }; @@ -214,22 +440,51 @@ goog.provide = function(name) { * 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). + * 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). * * @param {string=} opt_message Optional message to add to the error that's * raised when used in production code. */ goog.setTestOnly = function(opt_message) { - if (COMPILED && !goog.DEBUG) { + if (goog.DISALLOW_TEST_ONLY_CODE) { opt_message = opt_message || ''; throw Error('Importing test-only code into non-debug environment' + - opt_message ? ': ' + opt_message : '.'); + (opt_message ? ': ' + opt_message : '.')); } }; +/** + * 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. + * + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + */ +goog.forwardDeclare = function(name) {}; + + +/** + * Forward declare type information. Used to assign types to goog.global + * referenced object that would otherwise result in unknown type references + * and thus block property disambiguation. + */ +goog.forwardDeclare('Document'); +goog.forwardDeclare('HTMLScriptElement'); +goog.forwardDeclare('XMLHttpRequest'); + + if (!COMPILED) { /** @@ -240,25 +495,33 @@ if (!COMPILED) { * @private */ goog.isProvided_ = function(name) { - return !goog.implicitNamespaces_[name] && !!goog.getObjectByName(name); + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && + goog.isDefAndNotNull(goog.getObjectByName(name))); }; /** * Namespaces implicitly defined by goog.provide. For example, - * goog.provide('goog.events.Event') implicitly declares - * that 'goog' and 'goog.events' must be namespaces. + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. * - * @type {Object} + * @type {!Object} * @private */ - goog.implicitNamespaces_ = {}; + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. } /** - * Returns an object based on its fully qualified external name. If you are - * using a compilation pass that renames property names beware that using this - * function will not find renamed properties. + * 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. * * @param {string} name The fully qualified name. * @param {Object=} opt_obj The object within which to look; default is @@ -282,7 +545,7 @@ goog.getObjectByName = function(name, opt_obj) { /** * Globalizes a whole namespace, such as goog or goog.lang. * - * @param {Object} obj The namespace to globalize. + * @param {!Object} obj The namespace to globalize. * @param {Object=} opt_global The object to add the properties to. * @deprecated Properties may be explicitly exported to the global scope, but * this should no longer be done in bulk. @@ -298,22 +561,21 @@ goog.globalize = function(obj, opt_global) { /** * Adds a dependency from a file to the files it requires. * @param {string} relPath The path to the js file. - * @param {Array} provides An array of strings with the names of the objects - * this file provides. - * @param {Array} requires An array of strings with the names of the objects - * this file requires. - */ -goog.addDependency = function(relPath, provides, requires) { + * @param {!Array} provides An array of strings with + * the names of the objects this file provides. + * @param {!Array} requires An array of strings with + * the names of the objects this file requires. + * @param {boolean=} opt_isModule Whether this dependency must be loaded as + * a module as declared by goog.module. + */ +goog.addDependency = function(relPath, provides, requires, opt_isModule) { if (goog.DEPENDENCIES_ENABLED) { var provide, require; var path = relPath.replace(/\\/g, '/'); var deps = goog.dependencies_; for (var i = 0; provide = provides[i]; i++) { deps.nameToPath[provide] = path; - if (!(path in deps.pathToNames)) { - deps.pathToNames[path] = {}; - } - deps.pathToNames[path][provide] = true; + deps.pathIsModule[path] = !!opt_isModule; } for (var j = 0; require = requires[j]; j++) { if (!(path in deps.requires)) { @@ -327,17 +589,17 @@ goog.addDependency = function(relPath, provides, requires) { -// NOTE(nnaze): The debug DOM loader was included in base.js as an orignal -// way to do "debug-mode" development. The dependency system can sometimes -// be confusing, as can the debug DOM loader's asyncronous nature. +// NOTE(nnaze): The debug DOM loader was included in base.js as an original way +// to do "debug-mode" development. The dependency system can sometimes be +// confusing, as can the debug DOM loader's asynchronous nature. // -// With the DOM loader, a call to goog.require() is not blocking -- the -// script will not load until some point after the current script. If a -// namespace is needed at runtime, it needs to be defined in a previous -// script, or loaded via require() with its registered dependencies. +// With the DOM loader, a call to goog.require() is not blocking -- the script +// will not load until some point after the current script. If a namespace is +// needed at runtime, it needs to be defined in a previous script, or loaded via +// require() with its registered dependencies. // User-defined namespaces may need their own deps file. See http://go/js_deps, // http://go/genjsdeps, or, externally, DepsWriter. -// http://code.google.com/closure/library/docs/depswriter.html +// https://developers.google.com/closure/library/docs/depswriter // // Because of legacy clients, the DOM loader can't be easily removed from // base.js. Work is being done to make it disableable or replaceable for @@ -359,50 +621,60 @@ goog.define('goog.ENABLE_DEBUG_LOADER', true); /** - * 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. + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + +/** + * 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 --process_closure_primitives option is + * used. * @see goog.provide - * @param {string} name Namespace to include (as was given in goog.provide()) - * in the form "goog.package.part". + * @param {string} name Namespace to include (as was given in goog.provide()) in + * the form "goog.package.part". + * @return {?} If called within a goog.module file, the associated namespace or + * module otherwise null. */ goog.require = function(name) { - - // if the object already exists we do not need do do anything - // TODO(arv): If we start to support require based on file name this has - // to change - // TODO(arv): If we allow goog.foo.* this has to change - // TODO(arv): If we implement dynamic load after page load we should probably - // not remove this code for the compiled output + // If the object already exists we do not need do do anything. if (!COMPILED) { + if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) { + goog.maybeProcessDeferredDep_(name); + } + if (goog.isProvided_(name)) { - return; + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(name); + } else { + return null; + } } if (goog.ENABLE_DEBUG_LOADER) { var path = goog.getPathFromDeps_(name); if (path) { - goog.included_[path] = true; - goog.writeScripts_(); - return; + goog.writeScripts_(path); + return null; } } var errorMessage = 'goog.require could not find: ' + name; - if (goog.global.console) { - goog.global.console['error'](errorMessage); - } - - - throw Error(errorMessage); + goog.logToConsole_(errorMessage); + throw Error(errorMessage); } }; /** - * Path for included scripts + * Path for included scripts. * @type {string} */ goog.basePath = ''; @@ -416,8 +688,7 @@ goog.global.CLOSURE_BASE_PATH; /** - * Whether to write out Closure's deps file. By default, - * the deps are written. + * Whether to write out Closure's deps file. By default, the deps are written. * @type {boolean|undefined} */ goog.global.CLOSURE_NO_DEPS; @@ -431,6 +702,7 @@ goog.global.CLOSURE_NO_DEPS; * * The function is passed the script source, which is a relative URI. It should * return true if the script was imported, false otherwise. + * @type {(function(string): boolean)|undefined} */ goog.global.CLOSURE_IMPORT_SCRIPT; @@ -442,35 +714,18 @@ goog.global.CLOSURE_IMPORT_SCRIPT; goog.nullFunction = function() {}; -/** - * The identity function. Returns its first argument. - * - * @param {*=} opt_returnValue The single value that will be returned. - * @param {...*} var_args Optional trailing arguments. These are ignored. - * @return {?} The first argument. We can't know the type -- just pass it along - * without type. - * @deprecated Use goog.functions.identity instead. - */ -goog.identityFunction = function(opt_returnValue, var_args) { - return opt_returnValue; -}; - - /** * 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. + * 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. + * 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. * * @type {!Function} - * @throws {Error} when invoked to indicate the method should be - * overridden. + * @throws {Error} when invoked to indicate the method should be overridden. */ goog.abstractMethod = function() { throw Error('unimplemented abstract method'); @@ -478,8 +733,8 @@ goog.abstractMethod = function() { /** - * Adds a {@code getInstance} static method that always return the same instance - * object. + * Adds a {@code getInstance} static method that always returns the same + * instance object. * @param {!Function} ctor The constructor for the class to add the static * method to. */ @@ -501,12 +756,37 @@ goog.addSingletonGetter = function(ctor) { * All singleton classes that have been instantiated, for testing. Don't read * it directly, use the {@code goog.testing.singleton} module. The compiler * removes this variable if unused. - * @type {!Array.} + * @type {!Array} * @private */ goog.instantiatedSingletons_ = []; +/** + * @define {boolean} Whether to load goog.modules using {@code eval} when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or similar. + * However in some environments the use of {@code eval} is banned + * so we provide an alternative. + */ +goog.define('goog.LOAD_MODULE_USING_EVAL', true); + + +/** + * @define {boolean} Whether the exports of goog.modules should be sealed when + * possible. + */ +goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); + + +/** + * The registry of initialized modules: + * the module identifier to module exports map. + * @private @const {!Object} + */ +goog.loadedModules_ = {}; + + /** * True if goog.dependencies_ is available. * @const {boolean} @@ -515,29 +795,33 @@ goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; if (goog.DEPENDENCIES_ENABLED) { - /** - * Object used to keep track of urls that have already been added. This - * record allows the prevention of circular dependencies. - * @type {Object} - * @private - */ - goog.included_ = {}; - /** * This object is used to keep track of dependencies and other data that is - * used for loading scripts + * used for loading scripts. * @private - * @type {Object} + * @type {{ + * pathIsModule: !Object, + * nameToPath: !Object, + * requires: !Object>, + * visited: !Object, + * written: !Object, + * deferred: !Object + * }} */ goog.dependencies_ = { - pathToNames: {}, // 1 to many + pathIsModule: {}, // 1 to 1 + nameToPath: {}, // 1 to 1 + requires: {}, // 1 to many - // used when resolving dependencies to prevent us from - // visiting the file twice + + // Used when resolving dependencies to prevent us from visiting file twice. visited: {}, - written: {} // used to keep track of script files we have written + + written: {}, // Used to keep track of script files we have written. + + deferred: {} // Used to track deferred module evaluations in old IEs }; @@ -547,29 +831,31 @@ if (goog.DEPENDENCIES_ENABLED) { * @private */ goog.inHtmlDocument_ = function() { + /** @type {Document} */ var doc = goog.global.document; - return typeof doc != 'undefined' && - 'write' in doc; // XULDocument misses write. + return doc != null && 'write' in doc; // XULDocument misses write. }; /** - * Tries to detect the base path of the base.js script that bootstraps Closure + * Tries to detect the base path of base.js script that bootstraps Closure. * @private */ goog.findBasePath_ = function() { - if (goog.global.CLOSURE_BASE_PATH) { + if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) { goog.basePath = goog.global.CLOSURE_BASE_PATH; return; } else if (!goog.inHtmlDocument_()) { return; } + /** @type {Document} */ var doc = goog.global.document; - var scripts = doc.getElementsByTagName('script'); + var scripts = doc.getElementsByTagName('SCRIPT'); // Search backwards since the current script is in almost all cases the one // that has base.js. for (var i = scripts.length - 1; i >= 0; --i) { - var src = scripts[i].src; + var script = /** @type {!HTMLScriptElement} */ (scripts[i]); + var src = script.src; var qmark = src.lastIndexOf('?'); var l = qmark == -1 ? src.length : qmark; if (src.substr(l - 7, 7) == 'base.js') { @@ -584,33 +870,327 @@ if (goog.DEPENDENCIES_ENABLED) { * Imports a script if, and only if, that script hasn't already been imported. * (Must be called at execution time) * @param {string} src Script source. + * @param {string=} opt_sourceText The optionally source text to evaluate * @private */ - goog.importScript_ = function(src) { + goog.importScript_ = function(src, opt_sourceText) { var importScript = goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_; - if (!goog.dependencies_.written[src] && importScript(src)) { + if (importScript(src, opt_sourceText)) { + goog.dependencies_.written[src] = true; + } + }; + + + /** @const @private {boolean} */ + goog.IS_OLD_IE_ = !!(!goog.global.atob && goog.global.document && + goog.global.document.all); + + + /** + * Given a URL initiate retrieval and execution of the module. + * @param {string} src Script source URL. + * @private + */ + goog.importModule_ = function(src) { + // In an attempt to keep browsers from timing out loading scripts using + // synchronous XHRs, put each load in its own script block. + var bootstrap = 'goog.retrieveAndExecModule_("' + src + '");'; + + if (goog.importScript_('', bootstrap)) { goog.dependencies_.written[src] = true; } }; + /** @private {!Array} */ + goog.queuedModules_ = []; + + + /** + * Return an appropriate module text. Suitable to insert into + * a script tag (that is unescaped). + * @param {string} srcUrl + * @param {string} scriptText + * @return {string} + * @private + */ + goog.wrapModule_ = function(srcUrl, scriptText) { + if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) { + return '' + + 'goog.loadModule(function(exports) {' + + '"use strict";' + + scriptText + + '\n' + // terminate any trailing single line comment. + ';return exports' + + '});' + + '\n//# sourceURL=' + srcUrl + '\n'; + } else { + return '' + + 'goog.loadModule(' + + goog.global.JSON.stringify( + scriptText + '\n//# sourceURL=' + srcUrl + '\n') + + ');'; + } + }; + + // On IE9 and earlier, it is necessary to handle + // deferred module loads. In later browsers, the + // code to be evaluated is simply inserted as a script + // block in the correct order. To eval deferred + // code at the right time, we piggy back on goog.require to call + // goog.maybeProcessDeferredDep_. + // + // The goog.requires are used both to bootstrap + // the loading process (when no deps are available) and + // declare that they should be available. + // + // Here we eval the sources, if all the deps are available + // either already eval'd or goog.require'd. This will + // be the case when all the dependencies have already + // been loaded, and the dependent module is loaded. + // + // But this alone isn't sufficient because it is also + // necessary to handle the case where there is no root + // that is not deferred. For that there we register for an event + // and trigger goog.loadQueuedModules_ handle any remaining deferred + // evaluations. + + /** + * Handle any remaining deferred goog.module evals. + * @private + */ + goog.loadQueuedModules_ = function() { + var count = goog.queuedModules_.length; + if (count > 0) { + var queue = goog.queuedModules_; + goog.queuedModules_ = []; + for (var i = 0; i < count; i++) { + var path = queue[i]; + goog.maybeProcessDeferredPath_(path); + } + } + }; + + + /** + * Eval the named module if its dependencies are + * available. + * @param {string} name The module to load. + * @private + */ + goog.maybeProcessDeferredDep_ = function(name) { + if (goog.isDeferredModule_(name) && + goog.allDepsAreAvailable_(name)) { + var path = goog.getPathFromDeps_(name); + goog.maybeProcessDeferredPath_(goog.basePath + path); + } + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose evaluation has been deferred. + * @private + */ + goog.isDeferredModule_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && goog.dependencies_.pathIsModule[path]) { + var abspath = goog.basePath + path; + return (abspath) in goog.dependencies_.deferred; + } + return false; + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose declared dependencies have all been loaded + * (eval'd or a deferred module load) + * @private + */ + goog.allDepsAreAvailable_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && (path in goog.dependencies_.requires)) { + for (var requireName in goog.dependencies_.requires[path]) { + if (!goog.isProvided_(requireName) && + !goog.isDeferredModule_(requireName)) { + return false; + } + } + } + return true; + }; + + + /** + * @param {string} abspath + * @private + */ + goog.maybeProcessDeferredPath_ = function(abspath) { + if (abspath in goog.dependencies_.deferred) { + var src = goog.dependencies_.deferred[abspath]; + delete goog.dependencies_.deferred[abspath]; + goog.globalEval(src); + } + }; + + + /** + * Load a goog.module from the provided URL. This is not a general purpose + * code loader and does not support late loading code, that is it should only + * be used during page load. This method exists to support unit tests and + * "debug" loaders that would otherwise have inserted script tags. Under the + * hood this needs to use a synchronous XHR and is not recommeneded for + * production code. + * + * The module's goog.requires must have already been satisified; an exception + * will be thrown if this is not the case. This assumption is that no + * "deps.js" file exists, so there is no way to discover and locate the + * module-to-be-loaded's dependencies and no attempt is made to do so. + * + * There should only be one attempt to load a module. If + * "goog.loadModuleFromUrl" is called for an already loaded module, an + * exception will be throw. + * + * @param {string} url The URL from which to attempt to load the goog.module. + */ + goog.loadModuleFromUrl = function(url) { + // Because this executes synchronously, we don't need to do any additional + // bookkeeping. When "goog.loadModule" the namespace will be marked as + // having been provided which is sufficient. + goog.retrieveAndExecModule_(url); + }; + + + /** + * @param {function(?):?|string} moduleDef The module definition. + */ + goog.loadModule = function(moduleDef) { + // NOTE: we allow function definitions to be either in the from + // of a string to eval (which keeps the original source intact) or + // in a eval forbidden environment (CSP) we allow a function definition + // which in its body must call {@code goog.module}, and return the exports + // of the module. + var previousState = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = { + moduleName: undefined, + declareLegacyNamespace: false + }; + var exports; + if (goog.isFunction(moduleDef)) { + exports = moduleDef.call(goog.global, {}); + } else if (goog.isString(moduleDef)) { + exports = goog.loadModuleFromSource_.call(goog.global, moduleDef); + } else { + throw Error('Invalid module definition'); + } + + var moduleName = goog.moduleLoaderState_.moduleName; + if (!goog.isString(moduleName) || !moduleName) { + throw Error('Invalid module name \"' + moduleName + '\"'); + } + + // Don't seal legacy namespaces as they may be uses as a parent of + // another namespace + if (goog.moduleLoaderState_.declareLegacyNamespace) { + goog.constructNamespace_(moduleName, exports); + } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) { + Object.seal(exports); + } + + goog.loadedModules_[moduleName] = exports; + } finally { + goog.moduleLoaderState_ = previousState; + } + }; + + + /** + * @private @const {function(string):?} + * + * The new type inference warns because this function has no formal + * parameters, but its jsdoc says that it takes one argument. + * (The argument is used via arguments[0], but NTI does not detect this.) + * @suppress {newCheckTypes} + */ + goog.loadModuleFromSource_ = function() { + // NOTE: we avoid declaring parameters or local variables here to avoid + // masking globals or leaking values into the module definition. + 'use strict'; + var exports = {}; + eval(arguments[0]); + return exports; + }; + + + /** + * Writes a new script pointing to {@code src} directly into the DOM. + * + * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for + * the fallback mechanism. + * + * @param {string} src The script URL. + * @private + */ + goog.writeScriptSrcNode_ = function(src) { + goog.global.document.write( + '':a=e.settings.video_template_callback?e.settings.video_template_callback(o):'"}return a}function s(e){var t={};return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!0,special:"script,noscript",start:function(e,i){if(t.source1||"param"!=e||(t.source1=i.map.movie),("iframe"==e||"object"==e||"embed"==e||"video"==e||"audio"==e)&&(t.type||(t.type=e),t=tinymce.extend(i.map,t)),"script"==e){var o=r(i.map.src);if(!o)return;t={type:"script",source1:i.map.src,width:o.width,height:o.height}}"source"==e&&(t.source1?t.source2||(t.source2=i.map.src):t.source1=i.map.src),"img"!=e||t.poster||(t.poster=i.map.src)}}).parse(e),t.source1=t.source1||t.src||t.data,t.source2=t.source2||"",t.poster=t.poster||"",t}function n(t){return t.getAttribute("data-mce-object")?s(e.serializer.serialize(t,{selection:!0})):{}}function m(e,t,i){function r(e,t){var i,r,o,a;for(i in t)if(o=""+t[i],e.map[i])for(r=e.length;r--;)a=e[r],a.name==i&&(o?(e.map[i]=o,a.value=o):(delete e.map[i],e.splice(r,1)));else o&&(e.push({name:i,value:o}),e.map[i]=o)}var o,a=new tinymce.html.Writer,c=0;return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!0,special:"script,noscript",comment:function(e){a.comment(e)},cdata:function(e){a.cdata(e)},text:function(e,t){a.text(e,t)},start:function(e,s,n){switch(e){case"video":case"object":case"embed":case"img":case"iframe":r(s,{width:t.width,height:t.height})}if(i)switch(e){case"video":r(s,{poster:t.poster,src:""}),t.source2&&r(s,{src:""});break;case"iframe":r(s,{src:t.source1});break;case"source":if(c++,2>=c&&(r(s,{src:t["source"+c],type:t["source"+c+"mime"]}),!t["source"+c]))return;break;case"img":if(!t.poster)return;o=!0}a.start(e,s,n)},end:function(e){if("video"==e&&i)for(var s=1;2>=s;s++)if(t["source"+s]){var n=[];n.map={},s>c&&(r(n,{src:t["source"+s],type:t["source"+s+"mime"]}),a.start("source",n,!0))}if(t.poster&&"object"==e&&i&&!o){var m=[];m.map={},r(m,{src:t.poster,width:t.width,height:t.height}),a.start("img",m,!0)}a.end(e)}},new tinymce.html.Schema({})).parse(e),a.getContent()}var u=[{regex:/youtu\.be\/([\w\-.]+)/,type:"iframe",w:425,h:350,url:"//www.youtube.com/embed/$1"},{regex:/youtube\.com(.+)v=([^&]+)/,type:"iframe",w:425,h:350,url:"//www.youtube.com/embed/$2"},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc"},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'//maps.google.com/maps/ms?msid=$2&output=embed"'}];e.on("ResolveName",function(e){var t;1==e.target.nodeType&&(t=e.target.getAttribute("data-mce-object"))&&(e.name=t)}),e.on("preInit",function(){var t=e.schema.getSpecialElements();tinymce.each("video audio iframe object".split(" "),function(e){t[e]=new RegExp("]*>","gi")});var i=e.schema.getBoolAttrs();tinymce.each("webkitallowfullscreen mozallowfullscreen allowfullscreen".split(" "),function(e){i[e]={}}),e.parser.addNodeFilter("iframe,video,audio,object,embed,script",function(t,i){for(var o,a,c,s,n,m,u,d,l=t.length;l--;)if(a=t[l],a.parent&&("script"!=a.name||(d=r(a.attr("src"))))){for(c=new tinymce.html.Node("img",1),c.shortEnded=!0,d&&(d.width&&a.attr("width",d.width.toString()),d.height&&a.attr("height",d.height.toString())),m=a.attributes,o=m.length;o--;)s=m[o].name,n=m[o].value,"width"!==s&&"height"!==s&&"style"!==s&&(("data"==s||"src"==s)&&(n=e.convertURL(n,s)),c.attr("data-mce-p-"+s,n));u=a.firstChild&&a.firstChild.value,u&&(c.attr("data-mce-html",escape(u)),c.firstChild=null),c.attr({width:a.attr("width")||"300",height:a.attr("height")||("audio"==i?"30":"150"),style:a.attr("style"),src:tinymce.Env.transparentSrc,"data-mce-object":i,"class":"mce-object mce-object-"+i}),a.replace(c)}}),e.serializer.addAttributeFilter("data-mce-object",function(e,t){for(var i,r,o,a,c,s,n,m=e.length;m--;)if(i=e[m],i.parent){for(n=i.attr(t),r=new tinymce.html.Node(n,1),"audio"!=n&&"script"!=n&&r.attr({width:i.attr("width"),height:i.attr("height")}),r.attr({style:i.attr("style")}),a=i.attributes,o=a.length;o--;){var u=a[o].name;0===u.indexOf("data-mce-p-")&&r.attr(u.substr(11),a[o].value)}"script"==n&&r.attr("type","text/javascript"),c=i.attr("data-mce-html"),c&&(s=new tinymce.html.Node("#text",3),s.raw=!0,s.value=unescape(c),r.append(s)),i.replace(r)}})}),e.on("ObjectSelected",function(e){var t=e.target.getAttribute("data-mce-object");("audio"==t||"script"==t)&&e.preventDefault()}),e.on("objectResized",function(e){var t,i=e.target;i.getAttribute("data-mce-object")&&(t=i.getAttribute("data-mce-html"),t&&(t=unescape(t),i.setAttribute("data-mce-html",escape(m(t,{width:e.width,height:e.height})))))}),e.addButton("media",{tooltip:"Insert/edit video",onclick:o,stateSelector:["img[data-mce-object=video]","img[data-mce-object=iframe]"]}),e.addMenuItem("media",{icon:"media",text:"Insert video",onclick:o,context:"insert",prependToContext:!0})});tinymce.PluginManager.add("nonbreaking",function(n){var e=n.getParam("nonbreaking_force_tab");if(n.addCommand("mceNonBreaking",function(){n.insertContent(n.plugins.visualchars&&n.plugins.visualchars.state?' ':" "),n.dom.setAttrib(n.dom.select("span.mce-nbsp"),"data-mce-bogus","1")}),n.addButton("nonbreaking",{title:"Insert nonbreaking space",cmd:"mceNonBreaking"}),n.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),e){var a=+e>1?+e:3;n.on("keydown",function(e){if(9==e.keyCode){if(e.shiftKey)return;e.preventDefault();for(var t=0;a>t;t++)n.execCommand("mceNonBreaking")}})}});tinymce.PluginManager.add("noneditable",function(e){function t(e){var t;if(1===e.nodeType){if(t=e.getAttribute(u),t&&"inherit"!==t)return t;if(t=e.contentEditable,"inherit"!==t)return t}return null}function n(e){for(var n;e;){if(n=t(e))return"false"===n?e:null;e=e.parentNode}}function r(){function r(e){for(;e;){if(e.id===g)return e;e=e.parentNode}}function a(e){var t;if(e)for(t=new f(e,e),e=t.current();e;e=t.next())if(3===e.nodeType)return e}function i(n,r){var a,i;return"false"===t(n)&&u.isBlock(n)?void s.select(n):(i=u.createRng(),"true"===t(n)&&(n.firstChild||n.appendChild(e.getDoc().createTextNode(" ")),n=n.firstChild,r=!0),a=u.create("span",{id:g,"data-mce-bogus":!0},m),r?n.parentNode.insertBefore(a,n):u.insertAfter(a,n),i.setStart(a.firstChild,1),i.collapse(!0),s.setRng(i),a)}function o(e){var t,n,i,o;if(e)t=s.getRng(!0),t.setStartBefore(e),t.setEndBefore(e),n=a(e),n&&n.nodeValue.charAt(0)==m&&(n=n.deleteData(0,1)),u.remove(e,!0),s.setRng(t);else for(i=r(s.getStart());(e=u.get(g))&&e!==o;)i!==e&&(n=a(e),n&&n.nodeValue.charAt(0)==m&&(n=n.deleteData(0,1)),u.remove(e,!0)),o=e}function l(){function e(e,n){var r,a,i,o,l;if(r=d.startContainer,a=d.startOffset,3==r.nodeType){if(l=r.nodeValue.length,a>0&&l>a||(n?a==l:0===a))return}else{if(!(a0?a-1:a;r=r.childNodes[u],r.hasChildNodes()&&(r=r.firstChild)}for(i=new f(r,e);o=i[n?"prev":"next"]();){if(3===o.nodeType&&o.nodeValue.length>0)return;if("true"===t(o))return o}return e}var r,a,l,d,u;o(),l=s.isCollapsed(),r=n(s.getStart()),a=n(s.getEnd()),(r||a)&&(d=s.getRng(!0),l?(r=r||a,(u=e(r,!0))?i(u,!0):(u=e(r,!1))?i(u,!1):s.select(r)):(d=s.getRng(!0),r&&d.setStartBefore(r),a&&d.setEndAfter(a),s.setRng(d)))}function d(a){function i(e,t){for(;e=e[t?"previousSibling":"nextSibling"];)if(3!==e.nodeType||e.nodeValue.length>0)return e}function d(e,t){s.select(e),s.collapse(t)}function g(a){function i(e){for(var t=d;t;){if(t===e)return;t=t.parentNode}u.remove(e),l()}function o(){var r,o,l=e.schema.getNonEmptyElements();for(o=new tinymce.dom.TreeWalker(d,e.getBody());(r=a?o.prev():o.next())&&!l[r.nodeName.toLowerCase()]&&!(3===r.nodeType&&tinymce.trim(r.nodeValue).length>0);)if("false"===t(r))return i(r),!0;return n(r)?!0:!1}var f,d,c,g;if(s.isCollapsed()){if(f=s.getRng(!0),d=f.startContainer,c=f.startOffset,d=r(d)||d,g=n(d))return i(g),!1;if(3==d.nodeType&&(a?c>0:ch||h>124)&&h!=c.DELETE&&h!=c.BACKSPACE){if((tinymce.isMac?a.metaKey:a.ctrlKey)&&(67==h||88==h||86==h))return;if(a.preventDefault(),h==c.LEFT||h==c.RIGHT){var y=h==c.LEFT;if(e.dom.isBlock(m)){var T=y?m.previousSibling:m.nextSibling,C=new f(T,T),b=y?C.prev():C.next();d(b,!y)}else d(m,y)}}else if(h==c.LEFT||h==c.RIGHT||h==c.BACKSPACE||h==c.DELETE){if(p=r(v)){if(h==c.LEFT||h==c.BACKSPACE)if(m=i(p,!0),m&&"false"===t(m)){if(a.preventDefault(),h!=c.LEFT)return void u.remove(m);d(m,!0)}else o(p);if(h==c.RIGHT||h==c.DELETE)if(m=i(p),m&&"false"===t(m)){if(a.preventDefault(),h!=c.RIGHT)return void u.remove(m);d(m,!1)}else o(p)}if((h==c.BACKSPACE||h==c.DELETE)&&!g(h==c.BACKSPACE))return a.preventDefault(),!1}}var u=e.dom,s=e.selection,g="mce_noneditablecaret",m="";e.on("mousedown",function(n){var r=e.selection.getNode();"false"===t(r)&&r==n.target&&l()}),e.on("mouseup keyup",l),e.on("keydown",d)}function a(t){var n=l.length,r=t.content,a=tinymce.trim(o);if("raw"!=t.format){for(;n--;)r=r.replace(l[n],function(t){var n=arguments,i=n[n.length-2];return i>0&&'"'==r.charAt(i-1)?t:''+e.dom.encode("string"==typeof n[1]?n[1]:n[0])+""});t.content=r}}var i,o,l,f=tinymce.dom.TreeWalker,d="contenteditable",u="data-mce-"+d,c=tinymce.util.VK;i=" "+tinymce.trim(e.getParam("noneditable_editable_class","mceEditable"))+" ",o=" "+tinymce.trim(e.getParam("noneditable_noneditable_class","mceNonEditable"))+" ",l=e.getParam("noneditable_regexp"),l&&!l.length&&(l=[l]),e.on("PreInit",function(){r(),l&&e.on("BeforeSetContent",a),e.parser.addAttributeFilter("class",function(e){for(var t,n,r=e.length;r--;)n=e[r],t=" "+n.attr("class")+" ",-1!==t.indexOf(i)?n.attr(u,"true"):-1!==t.indexOf(o)&&n.attr(u,"false")}),e.serializer.addAttributeFilter(u,function(e){for(var t,n=e.length;n--;)t=e[n],l&&t.attr("data-mce-content")?(t.name="#text",t.type=3,t.raw=!0,t.value=t.attr("data-mce-content")):(t.attr(d,null),t.attr(u,null))}),e.parser.addAttributeFilter(d,function(e){for(var t,n=e.length;n--;)t=e[n],t.attr(u,t.attr(d)),t.attr(d,null)})}),e.on("drop",function(e){n(e.target)&&e.preventDefault()})});tinymce.PluginManager.add("pagebreak",function(e){var a="mce-pagebreak",t=e.getParam("pagebreak_separator",""),n=new RegExp(t.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(e){return"\\"+e}),"gi"),r='';e.addCommand("mcePageBreak",function(){e.insertContent(e.settings.pagebreak_split_block?"

        "+r+"

        ":r)}),e.addButton("pagebreak",{title:"Page break",cmd:"mcePageBreak"}),e.addMenuItem("pagebreak",{text:"Page break",icon:"pagebreak",cmd:"mcePageBreak",context:"insert"}),e.on("ResolveName",function(t){"IMG"==t.target.nodeName&&e.dom.hasClass(t.target,a)&&(t.name="pagebreak")}),e.on("click",function(t){t=t.target,"IMG"===t.nodeName&&e.dom.hasClass(t,a)&&e.selection.select(t)}),e.on("BeforeSetContent",function(e){e.content=e.content.replace(n,r)}),e.on("PreInit",function(){e.serializer.addNodeFilter("img",function(a){for(var n,r,c=a.length;c--;)if(n=a[c],r=n.attr("class"),r&&-1!==r.indexOf("mce-pagebreak")){var o=n.parent;if(e.schema.getBlockElements()[o.name]&&e.settings.pagebreak_split_block){o.type=3,o.value=t,o.raw=!0,n.remove();continue}n.type=3,n.value=t,n.raw=!0}})})});!function(e,t){"use strict";function n(e,t){for(var n,i=[],r=0;r"),t&&/^(PRE|DIV)$/.test(t.nodeName)||!o?e=n.filter(e,[[/\n/g,"
        "]]):(e=n.filter(e,[[/\n\n/g,"

        "+a],[/^(.*<\/p>)(

        )$/,a+"$1"],[/\n/g,"
        "]]),-1!=e.indexOf("

        ")&&(e=a+e)),r(e)}function a(){var t=i.dom,n=i.getBody(),r=i.dom.getViewPort(i.getWin()),o=r.y,a=20,s;if(v=i.selection.getRng(),i.inline&&(s=i.selection.getScrollContainer(),s&&s.scrollTop>0&&(o=s.scrollTop)),v.getClientRects){var l=v.getClientRects();if(l.length)a=o+(l[0].top-t.getPos(n).y);else{a=o;var c=v.startContainer;c&&(3==c.nodeType&&c.parentNode!=n&&(c=c.parentNode),1==c.nodeType&&(a=t.getPos(c,s||n).y))}}h=t.add(i.getBody(),"div",{id:"mcepastebin",contentEditable:!0,"data-mce-bogus":"1",style:"position: absolute; top: "+a+"px;width: 10px; height: 10px; overflow: hidden; opacity: 0"},y),(e.ie||e.gecko)&&t.setStyle(h,"left","rtl"==t.getStyle(n,"direction",!0)?65535:-65535),t.bind(h,"beforedeactivate focusin focusout",function(e){e.stopPropagation()}),h.focus(),i.selection.select(h,!0)}function s(){if(h){for(var e;e=i.dom.get("mcepastebin");)i.dom.remove(e),i.dom.unbind(e);v&&i.selection.setRng(v)}x=!1,h=v=null}function l(){var e=y,t,n;for(t=i.dom.select("div[id=mcepastebin]"),n=t.length;n--;){var r=t[n].innerHTML;e==y&&(e=""),r.length>e.length&&(e=r)}return e}function c(e){var t={};if(e&&e.types){var n=e.getData("Text");n&&n.length>0&&(t["text/plain"]=n);for(var i=0;i')},t.readAsDataURL(e.getAsFile()),!0}}if(!(!i.settings.paste_data_images||"text/html"in t||"text/plain"in t)&&e.clipboardData){var o=e.clipboardData.items;if(o)for(var a=0;a0}function p(){i.on("keydown",function(n){if(!n.isDefaultPrevented()&&(t.metaKeyPressed(n)&&86==n.keyCode||n.shiftKey&&45==n.keyCode)){if(x=n.shiftKey&&86==n.keyCode,n.stopImmediatePropagation(),b=(new Date).getTime(),e.ie&&x)return n.preventDefault(),void i.fire("paste",{ieFake:!0});s(),a()}}),i.on("paste",function(t){var c=d(t),f=(new Date).getTime()-b<1e3,p="text"==g.pasteFormat||x;return t.isDefaultPrevented()?void s():u(t,c)?void s():(f||t.preventDefault(),!e.ie||f&&!t.ieFake||(a(),i.dom.bind(h,"paste",function(e){e.stopPropagation()}),i.getDoc().execCommand("Paste",!1,null),c["text/html"]=l()),void setTimeout(function(){var e=l();return h&&h.firstChild&&"mcepastebin"===h.firstChild.id&&(p=!0),s(),!p&&f&&e&&e!=y&&(c["text/html"]=e),e!=y&&f||(e=c["text/html"]||c["text/plain"]||y,e!=y)?(!m(c,"text/html")&&m(c,"text/plain")&&(p=!0),void(p?o(c["text/plain"]||n.innerText(e)):r(e))):void(f||i.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents."))},0))}),i.on("dragstart",function(e){if(e.dataTransfer.types)try{e.dataTransfer.setData("mce-internal",i.selection.getContent())}catch(t){}}),i.on("drop",function(e){var t=f(e);if(t&&!e.isDefaultPrevented()){var n=c(e.dataTransfer),a=n["mce-internal"]||n["text/html"]||n["text/plain"];a&&(e.preventDefault(),i.undoManager.transact(function(){n["mce-internal"]&&i.execCommand("Delete"),i.selection.setRng(t),n["text/html"]?r(a):o(a)}))}})}var g=this,h,v,b=0,y="%MCEPASTEBIN%",x;g.pasteHtml=r,g.pasteText=o,i.on("preInit",function(){p(),i.parser.addNodeFilter("img",function(t){if(!i.settings.paste_data_images)for(var n=t.length;n--;){var r=t[n].attributes.map.src;r&&0===r.indexOf("data:image")&&(t[n].attr("data-mce-object")||r===e.transparentSrc||t[n].remove())}})}),i.on("PreProcess",function(){i.dom.remove(i.dom.get("mcepastebin"))})}}),i(g,[c,d,u,h,v,l],function(e,t,n,i,r,o){function a(e){return/l?n&&(n=n.parent.parent):(i=n,n=null)),n&&n.name==a?n.append(e):(i=i||n,n=new r(a,1),s>1&&n.attr("start",""+s),e.wrap(n)),e.name="li",t.value="";var c=t.next;c&&3==c.type&&(c.value=c.value.replace(/^\u00a0+/,"")),l>o&&i&&i.lastChild.append(n),o=l}for(var n,i,o=1,a=e.getAll("p"),s=0;s/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/ /gi,"\xa0"],[/([\s\u00a0]*)<\/span>/gi,function(e,t){return t.length>0?t.replace(/./," ").slice(Math.floor(t.length/2)).split("").join("\xa0"):""}]]);var g=l.paste_word_valid_elements;g||(g="-strong/b,-em/i,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-p/div,-table[width],-tr,-td[colspan|rowspan|width],-th,-thead,-tfoot,-tbody,-a[href|name],sub,sup,strike,br,del");var h=new n({valid_elements:g,valid_children:"-li[p]"});e.each(h.elements,function(e){e.attributes["class"]||(e.attributes["class"]={},e.attributesOrder.push("class")),e.attributes.style||(e.attributes.style={},e.attributesOrder.push("style"))});var v=new t({},h);v.addAttributeFilter("style",function(e){for(var t=e.length,n;t--;)n=e[t],n.attr("style",u(n,n.attr("style"))),"span"==n.name&&n.parent&&!n.attributes.length&&n.unwrap()}),v.addAttributeFilter("class",function(e){for(var t=e.length,n,i;t--;)n=e[t],i=n.attr("class"),/^(MsoCommentReference|MsoCommentText|msoDel)$/i.test(i)&&n.remove(),n.attr("class",null)}),v.addNodeFilter("del",function(e){for(var t=e.length;t--;)e[t].remove()}),v.addNodeFilter("a",function(e){for(var t=e.length,n,i,r;t--;)if(n=e[t],i=n.attr("href"),r=n.attr("name"),i&&-1!=i.indexOf("#_msocom_"))n.remove();else if(i&&0===i.indexOf("file://")&&(i=i.split("#")[1],i&&(i="#"+i)),i||r){if(r&&!/^_?(?:toc|edn|ftn)/i.test(r)){n.unwrap();continue}n.attr({href:i,name:r})}else n.unwrap()});var b=v.parse(f);d(b),c.content=new i({},h).serialize(b)}})}return s.isWordContent=a,s}),i(b,[m,c,g,l],function(e,t,n,i){return function(r){function o(e){r.on("BeforePastePreProcess",function(t){t.content=e(t.content)})}function a(e){return e=i.filter(e,[/^[\s\S]*]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g,/|/g,[/\u00a0<\/span>/g,"\xa0"],/
        $/i])}function s(e){if(!n.isWordContent(e))return e;var o=[];t.each(r.schema.getBlockElements(),function(e,t){o.push(t)});var a=new RegExp("(?:
         [\\s\\r\\n]+|
        )*(<\\/?("+o.join("|")+")[^>]*>)(?:
         [\\s\\r\\n]+|
        )*","g");return e=i.filter(e,[[a,"$1"]]),e=i.filter(e,[[/

        /g,"

        "],[/
        /g," "],[/

        /g,"
        "]])}function l(e){if(n.isWordContent(e))return e;var t=r.settings.paste_webkit_styles;if(r.settings.paste_remove_styles_if_webkit===!1||"all"==t)return e;if(t&&(t=t.split(/[, ]/)),t){var i=r.dom,o=r.selection.getNode();e=e.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,function(e,n,r,a){var s=i.parseStyle(r,"span"),l={};if("none"===t)return n+a;for(var c=0;c]+) style="([^"]*)"([^>]*>)/gi,"$1$3");return e=e.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi,function(e,t,n,i){return t+' style="'+n+'"'+i})}e.webkit&&(o(l),o(a)),e.ie&&o(s)}}),i(y,[x,f,g,b],function(e,t,n,i){var r;e.add("paste",function(e){function o(){"text"==s.pasteFormat?(this.active(!1),s.pasteFormat="html"):(s.pasteFormat="text",this.active(!0),r||(e.windowManager.alert("Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off."),r=!0))}var a=this,s,l=e.settings;a.clipboard=s=new t(e),a.quirks=new i(e),a.wordFilter=new n(e),e.settings.paste_as_text&&(a.clipboard.pasteFormat="text"),l.paste_preprocess&&e.on("PastePreProcess",function(e){l.paste_preprocess.call(a,a,e)}),l.paste_postprocess&&e.on("PastePostProcess",function(e){l.paste_postprocess.call(a,a,e)}),e.addCommand("mceInsertClipboardContent",function(e,t){t.content&&a.clipboard.pasteHtml(t.content),t.text&&a.clipboard.pasteText(t.text)}),e.paste_block_drop&&e.on("dragend dragover draggesture dragdrop drop drag",function(e){e.preventDefault(),e.stopPropagation()}),e.settings.paste_data_images||e.on("drop",function(e){var t=e.dataTransfer;t&&t.files&&t.files.length>0&&e.preventDefault()}),e.addButton("pastetext",{icon:"pastetext",tooltip:"Paste as text",onclick:o,active:"text"==a.clipboard.pasteFormat}),e.addMenuItem("pastetext",{text:"Paste as text",selectable:!0,active:s.pasteFormat,onclick:o})})}),a([l,f,g,b,y])}(this);tinymce.PluginManager.add("preview",function(e){var t=e.settings,i=!tinymce.Env.ie;e.addCommand("mcePreview",function(){e.windowManager.open({title:"Preview",width:parseInt(e.getParam("plugin_preview_width","650"),10),height:parseInt(e.getParam("plugin_preview_height","500"),10),html:'",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var n,a="";a+='',tinymce.each(e.contentCSS,function(t){a+=''});var r=t.body_id||"tinymce";-1!=r.indexOf("=")&&(r=e.getParam("body_id","","hash"),r=r[e.id]||r);var d=t.body_class||"";-1!=d.indexOf("=")&&(d=e.getParam("body_class","","hash"),d=d[e.id]||"");var o=e.settings.directionality?' dir="'+e.settings.directionality+'"':"";if(n=""+a+'"+e.getContent()+"",i)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(n);else{var s=this.getEl("body").firstChild.contentWindow.document;s.open(),s.write(n),s.close()}}})}),e.addButton("preview",{title:"Preview",cmd:"mcePreview"}),e.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})});tinymce.PluginManager.add("print",function(t){t.addCommand("mcePrint",function(){t.getWin().print()}),t.addButton("print",{title:"Print",cmd:"mcePrint"}),t.addShortcut("Ctrl+P","","mcePrint"),t.addMenuItem("print",{text:"Print",cmd:"mcePrint",icon:"print",shortcut:"Ctrl+P",context:"file"})});tinymce.PluginManager.add("save",function(e){function a(){var a;return a=tinymce.DOM.getParent(e.id,"form"),!e.getParam("save_enablewhendirty",!0)||e.isDirty()?(tinymce.triggerSave(),e.getParam("save_onsavecallback")?void(e.execCallback("save_onsavecallback",e)&&(e.startContent=tinymce.trim(e.getContent({format:"raw"})),e.nodeChanged())):void(a?(e.isNotDirty=!0,(!a.onsubmit||a.onsubmit())&&("function"==typeof a.submit?a.submit():e.windowManager.alert("Error: Form submit field collision.")),e.nodeChanged()):e.windowManager.alert("Error: No form element found."))):void 0}function n(){var a=tinymce.trim(e.startContent);return e.getParam("save_oncancelcallback")?void e.execCallback("save_oncancelcallback",e):(e.setContent(a),e.undoManager.clear(),void e.nodeChanged())}function t(){var a=this;e.on("nodeChange",function(){a.disabled(e.getParam("save_enablewhendirty",!0)&&!e.isDirty())})}e.addCommand("mceSave",a),e.addCommand("mceCancel",n),e.addButton("save",{icon:"save",text:"Save",cmd:"mceSave",disabled:!0,onPostRender:t}),e.addButton("cancel",{text:"Cancel",icon:!1,cmd:"mceCancel",disabled:!0,onPostRender:t}),e.addShortcut("ctrl+s","","mceSave")});!function(){function e(e,t,n,a,r){function i(e,t){if(t=t||0,!e[0])throw"findAndReplaceDOMText cannot handle zero-length matches";var n=e.index;if(t>0){var a=e[t];if(!a)throw"Invalid capture group";n+=e[0].indexOf(a),e[0]=a}return[n,n+e[0].length,[e[0]]]}function d(e){var t;if(3===e.nodeType)return e.data;if(h[e.nodeName]&&!u[e.nodeName])return"";if(t="",(u[e.nodeName]||m[e.nodeName])&&(t+="\n"),e=e.firstChild)do t+=d(e);while(e=e.nextSibling);return t}function o(e,t,n){var a,r,i,d,o=[],l=0,c=e,s=t.shift(),f=0;e:for(;;){if((u[c.nodeName]||m[c.nodeName])&&l++,3===c.nodeType&&(!r&&c.length+l>=s[1]?(r=c,d=s[1]-l):a&&o.push(c),!a&&c.length+l>s[0]&&(a=c,i=s[0]-l),l+=c.length),a&&r){if(c=n({startNode:a,startNodeIndex:i,endNode:r,endNodeIndex:d,innerNodes:o,match:s[2],matchIndex:f}),l-=r.length-d,a=null,r=null,o=[],s=t.shift(),f++,!s)break}else{if((!h[c.nodeName]||u[c.nodeName])&&c.firstChild){c=c.firstChild;continue}if(c.nextSibling){c=c.nextSibling;continue}}for(;;){if(c.nextSibling){c=c.nextSibling;break}if(c.parentNode===e)break e;c=c.parentNode}}}function l(e){var t;if("function"!=typeof e){var n=e.nodeType?e:f.createElement(e);t=function(e,t){var a=n.cloneNode(!1);return a.setAttribute("data-mce-index",t),e&&a.appendChild(f.createTextNode(e)),a}}else t=e;return function(e){var n,a,r,i=e.startNode,d=e.endNode,o=e.matchIndex;if(i===d){var l=i;r=l.parentNode,e.startNodeIndex>0&&(n=f.createTextNode(l.data.substring(0,e.startNodeIndex)),r.insertBefore(n,l));var c=t(e.match[0],o);return r.insertBefore(c,l),e.endNodeIndexh;++h){var g=e.innerNodes[h],p=t(g.data,o);g.parentNode.replaceChild(p,g),u.push(p)}var x=t(d.data.substring(0,e.endNodeIndex),o);return r=i.parentNode,r.insertBefore(n,i),r.insertBefore(s,i),r.removeChild(i),r=d.parentNode,r.insertBefore(x,d),r.insertBefore(a,d),r.removeChild(d),x}}var c,s,f,u,h,m,g=[],p=0;if(f=t.ownerDocument,u=r.getBlockElements(),h=r.getWhiteSpaceElements(),m=r.getShortEndedElements(),s=d(t)){if(e.global)for(;c=e.exec(s);)g.push(i(c,a));else c=s.match(e),g.push(i(c,a));return g.length&&(p=g.length,o(t,g,l(n))),p}}function t(t){function n(){function e(){r.statusbar.find("#next").disabled(!d(s+1).length),r.statusbar.find("#prev").disabled(!d(s-1).length)}function n(){tinymce.ui.MessageBox.alert("Could not find the specified string.",function(){r.find("#find")[0].focus()})}var a={},r=tinymce.ui.Factory.create({type:"window",layout:"flex",pack:"center",align:"center",onClose:function(){t.focus(),c.done()},onSubmit:function(t){var i,o,l,f;return t.preventDefault(),o=r.find("#case").checked(),f=r.find("#words").checked(),l=r.find("#find").value(),l.length?a.text==l&&a.caseState==o&&a.wholeWord==f?0===d(s+1).length?void n():(c.next(),void e()):(i=c.find(l,o,f),i||n(),r.statusbar.items().slice(1).disabled(0===i),e(),void(a={text:l,caseState:o,wholeWord:f})):(c.done(!1),void r.statusbar.items().slice(1).disabled(!0))},buttons:[{text:"Find",onclick:function(){r.submit()}},{text:"Replace",disabled:!0,onclick:function(){c.replace(r.find("#replace").value())||(r.statusbar.items().slice(1).disabled(!0),s=-1,a={})}},{text:"Replace all",disabled:!0,onclick:function(){c.replace(r.find("#replace").value(),!0,!0),r.statusbar.items().slice(1).disabled(!0),a={}}},{type:"spacer",flex:1},{text:"Prev",name:"prev",disabled:!0,onclick:function(){c.prev(),e()}},{text:"Next",name:"next",disabled:!0,onclick:function(){c.next(),e()}}],title:"Find and replace",items:{type:"form",padding:20,labelGap:30,spacing:10,items:[{type:"textbox",name:"find",size:40,label:"Find",value:t.selection.getNode().src},{type:"textbox",name:"replace",size:40,label:"Replace with"},{type:"checkbox",name:"case",text:"Match case",label:" "},{type:"checkbox",name:"words",text:"Whole words",label:" "}]}}).renderTo().reflow()}function a(e){var t=e.getAttribute("data-mce-index");return"number"==typeof t?""+t:t}function r(n){var a,r;return r=t.dom.create("span",{"data-mce-bogus":1}),r.className="mce-match-marker",a=t.getBody(),c.done(!1),e(n,a,r,!1,t.schema)}function i(e){var t=e.parentNode;e.firstChild&&t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function d(e){var n,r=[];if(n=tinymce.toArray(t.getBody().getElementsByTagName("span")),n.length)for(var i=0;is&&f[o].setAttribute("data-mce-index",m-1)}return t.undoManager.add(),s=p,n?(g=d(p+1).length>0,c.next()):(g=d(p-1).length>0,c.prev()),!r&&g},c.done=function(e){var n,r,d,o;for(r=tinymce.toArray(t.getBody().getElementsByTagName("span")),n=0;n=d.end?(r=c,a=d.end-s):o&&l.push(c),!o&&c.length+s>d.start&&(o=c,i=d.start-s),s+=c.length),o&&r){if(c=n({startNode:o,startNodeIndex:i,endNode:r,endNodeIndex:a,innerNodes:l,match:d.text,matchIndex:u}),s-=r.length-a,o=null,r=null,l=[],d=t.shift(),u++,!d)break}else{if((!P[c.nodeName]||S[c.nodeName])&&c.firstChild){c=c.firstChild;continue}if(c.nextSibling){c=c.nextSibling;continue}}for(;;){if(c.nextSibling){c=c.nextSibling;break}if(c.parentNode===e)break e;c=c.parentNode}}}function i(e){function t(t,n){var o=w[n];o.stencil||(o.stencil=e(o));var r=o.stencil.cloneNode(!1);return r.setAttribute("data-mce-index",n),t&&r.appendChild(k.doc.createTextNode(t)),r}return function(e){var n,o,r,i=e.startNode,a=e.endNode,l=e.matchIndex,s=k.doc;if(i===a){var c=i;r=c.parentNode,e.startNodeIndex>0&&(n=s.createTextNode(c.data.substring(0,e.startNodeIndex)),r.insertBefore(n,c));var d=t(e.match,l);return r.insertBefore(d,c),e.endNodeIndexm;++m){var g=e.innerNodes[m],h=t(g.data,l);g.parentNode.replaceChild(h,g),f.push(h)}var v=t(a.data.substring(0,e.endNodeIndex),l);return r=i.parentNode,r.insertBefore(n,i),r.insertBefore(u,i),r.removeChild(i),r=a.parentNode,r.insertBefore(v,a),r.insertBefore(o,a),r.removeChild(a),v}}function a(e){var t=e.parentNode;t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function l(t){var n=e.getElementsByTagName("*"),o=[];t="number"==typeof t?""+t:null;for(var r=0;rt&&e(w[t],t)!==!1;t++);return this}function u(t){return w.length&&r(e,w,i(t)),this}function f(e,t){if(C&&e.global)for(;x=e.exec(C);)w.push(n(x,t));return this}function m(e){var t,n=l(e?s(e):null);for(t=n.length;t--;)a(n[t]);return this}function p(e){return w[e.getAttribute("data-mce-index")]}function g(e){return l(s(e))[0]}function h(e,t,n){return w.push({start:e,end:e+t,text:C.substr(e,t),data:n}),this}function v(e){var n=l(s(e)),o=t.dom.createRng();return o.setStartBefore(n[0]),o.setEndAfter(n[n.length-1]),o}function b(e,n){var o=v(e);return o.deleteContents(),n.length>0&&o.insertNode(t.dom.doc.createTextNode(n)),o}function y(){return w.splice(0,w.length),m(),this}var x,w=[],C,k=t.dom,S,P,N;return S=t.schema.getBlockElements(),P=t.schema.getWhiteSpaceElements(),N=t.schema.getShortEndedElements(),C=o(e),{text:C,matches:w,each:d,filter:c,reset:y,matchFromElement:p,elementFromMatch:g,find:f,add:h,wrap:u,unwrap:m,replace:b,rangeFromMatch:v,indexOf:s}}}),o(c,[s,d,u,f,m,p,g,h],function(e,t,n,o,r,i,a,l){t.add("spellchecker",function(t,s){function c(){return C.textMatcher||(C.textMatcher=new e(t.getBody(),t)),C.textMatcher}function d(e,t){var o=[];return n.each(t,function(e){o.push({selectable:!0,text:e.name,data:e.value})}),o}function u(e){for(var t in e)return!1;return!0}function f(e,i){var a=[],l=k[e];n.each(l,function(e){a.push({text:e,onclick:function(){t.insertContent(t.dom.encode(e)),t.dom.remove(i),g()}})}),a.push.apply(a,[{text:"-"},{text:"Ignore",onclick:function(){h(e,i)}},{text:"Ignore all",onclick:function(){h(e,i,!0)}},{text:"Finish",onclick:v}]),P=new o({items:a,context:"contextmenu",onautohide:function(e){-1!=e.target.className.indexOf("spellchecker")&&e.preventDefault()},onhide:function(){P.remove(),P=null}}),P.renderTo(document.body);var s=r.DOM.getPos(t.getContentAreaContainer()),c=t.dom.getPos(i[0]),d=t.dom.getRoot();"BODY"==d.nodeName?(c.x-=d.ownerDocument.documentElement.scrollLeft||d.scrollLeft,c.y-=d.ownerDocument.documentElement.scrollTop||d.scrollTop):(c.x-=d.scrollLeft,c.y-=d.scrollTop),s.x+=c.x,s.y+=c.y,P.moveTo(s.x,s.y+i[0].offsetHeight)}function m(){return t.getParam("spellchecker_wordchar_pattern")||new RegExp('[^\\s!"#$%&()*+,-./:;<=>?@[\\]^_{|}`\xa7\xa9\xab\xae\xb1\xb6\xb7\xb8\xbb\xbc\xbd\xbe\xbf\xd7\xf7\xa4\u201d\u201c\u201e]+',"g")}function p(){function e(e){return t.setProgressState(!1),u(e)?(t.windowManager.alert("No misspellings found"),void(S=!1)):(k=e,c().find(m()).filter(function(t){return!!e[t.text]}).wrap(function(e){return t.dom.create("span",{"class":"mce-spellchecker-word","data-mce-bogus":1,"data-mce-word":e.text})}),void t.fire("SpellcheckStart"))}function n(e){t.windowManager.alert(e),t.setProgressState(!1),v()}function o(e,t,o){i.send({url:new a(s).toAbsolute(N.spellchecker_rpc_url),type:"post",content_type:"application/x-www-form-urlencoded",data:"text="+encodeURIComponent(t)+"&lang="+N.spellchecker_language,success:function(e){e=l.parse(e),e?e.error?n(e.error):o(e.words):n("Sever response wasn't proper JSON.")},error:function(e,t){n("Spellchecker request error: "+t.status)}})}if(S)return void v();v(),S=!0,t.setProgressState(!0);var r=N.spellchecker_callback||o;r.call(C,"spellcheck",c().text,e,n),t.focus()}function g(){t.dom.select("span.mce-spellchecker-word").length||v()}function h(e,o,r){t.selection.collapse(),r?n.each(t.dom.select("span.mce-spellchecker-word"),function(n){n.getAttribute("data-mce-word")==e&&t.dom.remove(n,!0)}):t.dom.remove(o,!0),g()}function v(){c().reset(),C.textMatcher=null,S&&(S=!1,t.fire("SpellcheckEnd"))}function b(e){var t=e.getAttribute("data-mce-index");return"number"==typeof t?""+t:t}function y(e){var o,r=[];if(o=n.toArray(t.getBody().getElementsByTagName("span")),o.length)for(var i=0;i0){var r=t.dom.createRng();r.setStartBefore(o[0]),r.setEndAfter(o[o.length-1]),t.selection.setRng(r),f(n.getAttribute("data-mce-word"),o)}}}),t.addMenuItem("spellchecker",{text:"Spellcheck",context:"tools",onclick:p,selectable:!0,onPostRender:function(){var e=this;t.on("SpellcheckStart SpellcheckEnd",function(){e.active(S)})}});var T={tooltip:"Spellcheck",onclick:p,onPostRender:function(){var e=this;t.on("SpellcheckStart SpellcheckEnd",function(){e.active(S)})}};w.length>1&&(T.type="splitbutton",T.menu=w,T.onshow=x,T.onselect=function(e){N.spellchecker_language=e.control.settings.data}),t.addButton("spellchecker",T),t.addCommand("mceSpellCheck",p),t.on("remove",function(){P&&(P.remove(),P=null)}),t.on("change",g),this.getTextMatcher=c,this.getWordCharPattern=m,this.getLanguage=function(){return N.spellchecker_language},N.spellchecker_language=N.spellchecker_language||N.language||"en"})}),a([s,c])}(this);tinymce.PluginManager.add("tabfocus",function(e){function n(e){9!==e.keyCode||e.ctrlKey||e.altKey||e.metaKey||e.preventDefault()}function t(n){function t(n){function t(e){return"BODY"===e.nodeName||"hidden"!=e.type&&"none"!=e.style.display&&"hidden"!=e.style.visibility&&t(e.parentNode)}function r(e){return e.tabIndex||"INPUT"==e.nodeName||"TEXTAREA"==e.nodeName}function c(e){return!r(e)&&"-1"!=e.getAttribute("tabindex")&&t(e)}if(u=i.select(":input:enabled,*[tabindex]:not(iframe)"),o(u,function(n,t){return n.id==e.id?(a=t,!1):void 0}),n>0){for(d=a+1;d=0;d--)if(c(u[d]))return u[d];return null}var a,u,c,d;if(!(9!==n.keyCode||n.ctrlKey||n.altKey||n.metaKey)&&(c=r(e.getParam("tab_focus",e.getParam("tabfocus_elements",":prev,:next"))),1==c.length&&(c[1]=c[0],c[0]=":prev"),u=n.shiftKey?":prev"==c[0]?t(-1):i.get(c[0]):":next"==c[1]?t(1):i.get(c[1]))){var y=tinymce.get(u.id||u.name);u.id&&y?y.focus():window.setTimeout(function(){tinymce.Env.webkit||window.focus(),u.focus()},10),n.preventDefault()}}var i=tinymce.DOM,o=tinymce.each,r=tinymce.explode;e.on("init",function(){e.inline&&tinymce.DOM.setAttrib(e.getBody(),"tabIndex",null)}),e.on("keyup",n),tinymce.Env.gecko?e.on("keypress keydown",t):e.on("keydown",t)});!function(e,t){"use strict";function n(e,t){for(var n,o=[],i=0;i "+t+" tr",a);i(n,function(n,r){r+=e,i(I.select("> td, > th",n),function(e,n){var i,a,l,s;if(A[r])for(;A[r][n];)n++;for(l=o(e,"rowspan"),s=o(e,"colspan"),a=r;r+l>a;a++)for(A[a]||(A[a]=[]),i=n;n+s>i;i++)A[a][i]={part:t,real:a==r&&i==n,elm:e,rowspan:l,colspan:s}})}),e+=n.length})}function s(e,t){return e=e.cloneNode(t),e.removeAttribute("id"),e}function c(e,t){var n;return n=A[t],n?n[e]:void 0}function d(e,t,n){e&&(n=parseInt(n,10),1===n?e.removeAttribute(t,1):e.setAttribute(t,n,1))}function u(e){return e&&(I.hasClass(e.elm,"mce-item-selected")||e==M)}function f(){var e=[];return i(a.rows,function(t){i(t.cells,function(n){return I.hasClass(n,"mce-item-selected")||M&&n==M.elm?(e.push(t),!1):void 0})}),e}function m(){var e=I.createRng();e.setStartAfter(a),e.setEndAfter(a),E.setRng(e),I.remove(a)}function p(t){var o,a={};return r.settings.table_clone_elements!==!1&&(a=e.makeMap((r.settings.table_clone_elements||"strong em b i span font h1 h2 h3 h4 h5 h6 p div").toUpperCase(),/[ ,]/)),e.walk(t,function(e){var r;return 3==e.nodeType?(i(I.getParents(e.parentNode,null,t).reverse(),function(e){a[e.nodeName]&&(e=s(e,!1),o?r&&r.appendChild(e):o=r=e,r=e)}),r&&(r.innerHTML=n.ie?" ":'
        '),!1):void 0},"childNodes"),t=s(t,!1),d(t,"rowSpan",1),d(t,"colSpan",1),o?t.appendChild(o):n.ie||(t.innerHTML='
        '),t}function g(){var e=I.createRng(),t;return i(I.select("tr",a),function(e){0===e.cells.length&&I.remove(e)}),0===I.select("tr",a).length?(e.setStartBefore(a),e.setEndBefore(a),E.setRng(e),void I.remove(a)):(i(I.select("thead,tbody,tfoot",a),function(e){0===e.rows.length&&I.remove(e)}),l(),void(B&&(t=A[Math.min(A.length-1,B.y)],t&&(E.select(t[Math.min(t.length-1,B.x)].elm,!0),E.collapse(!0)))))}function h(e,t,n,o){var i,r,a,l,s;for(i=A[t][e].elm.parentNode,a=1;n>=a;a++)if(i=I.getNext(i,"tr")){for(r=e;r>=0;r--)if(s=A[t+a][r].elm,s.parentNode==i){for(l=1;o>=l;l++)I.insertAfter(p(s),s);break}if(-1==r)for(l=1;o>=l;l++)i.insertBefore(p(i.cells[0]),i.cells[0])}}function v(){i(A,function(e,t){i(e,function(e,n){var i,r,a;if(u(e)&&(e=e.elm,i=o(e,"colspan"),r=o(e,"rowspan"),i>1||r>1)){for(d(e,"rowSpan",1),d(e,"colSpan",1),a=0;i-1>a;a++)I.insertAfter(p(e),e);h(n,t,r-1,i)}})})}function b(t,n,o){var r,a,s,f,m,p,h,b,y,w,x;if(t?(r=T(t),a=r.x,s=r.y,f=a+(n-1),m=s+(o-1)):(B=D=null,i(A,function(e,t){i(e,function(e,n){u(e)&&(B||(B={x:n,y:t}),D={x:n,y:t})})}),B&&(a=B.x,s=B.y,f=D.x,m=D.y)),b=c(a,s),y=c(f,m),b&&y&&b.part==y.part){for(v(),l(),b=c(a,s).elm,d(b,"colSpan",f-a+1),d(b,"rowSpan",m-s+1),h=s;m>=h;h++)for(p=a;f>=p;p++)A[h]&&A[h][p]&&(t=A[h][p].elm,t!=b&&(w=e.grep(t.childNodes),i(w,function(e){b.appendChild(e)}),w.length&&(w=e.grep(b.childNodes),x=0,i(w,function(e){"BR"==e.nodeName&&I.getAttrib(e,"data-mce-bogus")&&x++0&&A[n-1][l]&&(g=A[n-1][l].elm,h=o(g,"rowSpan"),h>1)){d(g,"rowSpan",h+1);continue}}else if(h=o(r,"rowspan"),h>1){d(r,"rowSpan",h+1);continue}m=p(r),d(m,"colSpan",r.colSpan),f.appendChild(m),a=r}f.hasChildNodes()&&(e?c.parentNode.insertBefore(f,c):I.insertAfter(f,c))}}function w(e){var t,n;i(A,function(n){return i(n,function(n,o){return u(n)&&(t=o,e)?!1:void 0}),e?!t:void 0}),i(A,function(i,r){var a,l,s;i[t]&&(a=i[t].elm,a!=n&&(s=o(a,"colspan"),l=o(a,"rowspan"),1==s?e?(a.parentNode.insertBefore(p(a),a),h(t,r,l-1,s)):(I.insertAfter(p(a),a),h(t,r,l-1,s)):d(a,"colSpan",a.colSpan+1),n=a))})}function x(){var t=[];i(A,function(n){i(n,function(n,r){u(n)&&-1===e.inArray(t,r)&&(i(A,function(e){var t=e[r].elm,n;n=o(t,"colSpan"),n>1?d(t,"colSpan",n-1):I.remove(t)}),t.push(r))})}),g()}function C(){function e(e){var t,n,r;t=I.getNext(e,"tr"),i(e.cells,function(e){var t=o(e,"rowSpan");t>1&&(d(e,"rowSpan",t-1),n=T(e),h(n.x,n.y,1,1))}),n=T(e.cells[0]),i(A[n.y],function(e){var t;e=e.elm,e!=r&&(t=o(e,"rowSpan"),1>=t?I.remove(e):d(e,"rowSpan",t-1),r=e)})}var t;t=f(),i(t.reverse(),function(t){e(t)}),g()}function P(){var e=f();return I.remove(e),g(),e}function S(){var e=f();return i(e,function(t,n){e[n]=s(t,!0)}),e}function R(e,t){var n=f(),o=n[t?0:n.length-1],r=o.cells.length;e&&(i(A,function(e){var t;return r=0,i(e,function(e){e.real&&(r+=e.colspan),e.elm.parentNode==o&&(t=1)}),t?!1:void 0}),t||e.reverse(),i(e,function(e){var n,i=e.cells.length,a;for(n=0;i>n;n++)a=e.cells[n],d(a,"colSpan",1),d(a,"rowSpan",1);for(n=i;r>n;n++)e.appendChild(p(e.cells[i-1]));for(n=r;i>n;n++)I.remove(e.cells[n]);t?o.parentNode.insertBefore(e,o):I.insertAfter(e,o)}),I.removeClass(I.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"))}function T(e){var t;return i(A,function(n,o){return i(n,function(n,i){return n.elm==e?(t={x:i,y:o},!1):void 0}),!t}),t}function k(e){B=T(e)}function N(){var e,t;return e=t=0,i(A,function(n,o){i(n,function(n,i){var r,a;u(n)&&(n=A[o][i],i>e&&(e=i),o>t&&(t=o),n.real&&(r=n.colspan-1,a=n.rowspan-1,r&&i+r>e&&(e=i+r),a&&o+a>t&&(t=o+a)))})}),{x:e,y:t}}function _(e){var t,n,o,i,r,a,l,s,c,d;if(D=T(e),B&&D){for(t=Math.min(B.x,D.x),n=Math.min(B.y,D.y),o=Math.max(B.x,D.x),i=Math.max(B.y,D.y),r=o,a=i,d=n;a>=d;d++)e=A[d][t],e.real||t-(e.colspan-1)=c;c++)e=A[n][c],e.real||n-(e.rowspan-1)=d;d++)for(c=t;o>=c;c++)e=A[d][c],e.real&&(l=e.colspan-1,s=e.rowspan-1,l&&c+l>r&&(r=c+l),s&&d+s>a&&(a=d+s));for(I.removeClass(I.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),d=n;a>=d;d++)for(c=t;r>=c;c++)A[d][c]&&I.addClass(A[d][c].elm,"mce-item-selected")}}var A,B,D,M,E=r.selection,I=E.dom;a=a||I.getParent(E.getStart(),"table"),l(),M=I.getParent(E.getStart(),"th,td"),M&&(B=T(M),D=N(),M=c(B.x,B.y)),e.extend(this,{deleteTable:m,split:v,merge:b,insertRow:y,insertCol:w,deleteCols:x,deleteRows:C,cutRows:P,copyRows:S,pasteRows:R,getPos:T,setStartCell:k,setEndCell:_})}}),o(u,[f,d,c],function(e,t,n){function o(e,t){return parseInt(e.getAttribute(t)||1,10)}var i=n.each;return function(n){function r(){function t(t){function r(e,o){var i=e?"previousSibling":"nextSibling",r=n.dom.getParent(o,"tr"),l=r[i];if(l)return h(n,o,l,e),t.preventDefault(),!0;var d=n.dom.getParent(r,"table"),u=r.parentNode,f=u.nodeName.toLowerCase();if("tbody"===f||f===(e?"tfoot":"thead")){var m=a(e,d,u,"tbody");if(null!==m)return s(e,m,o)}return c(e,r,i,d)}function a(e,t,o,i){var r=n.dom.select(">"+i,t),a=r.indexOf(o);if(e&&0===a||!e&&a===r.length-1)return l(e,t);if(-1===a){var s="thead"===o.tagName.toLowerCase()?0:r.length-1;return r[s]}return r[a+(e?-1:1)]}function l(e,t){var o=e?"thead":"tfoot",i=n.dom.select(">"+o,t);return 0!==i.length?i[0]:null}function s(e,o,i){var r=d(o,e);return r&&h(n,i,r,e),t.preventDefault(),!0}function c(e,o,i,a){var l=a[i];if(l)return u(l),!0;var s=n.dom.getParent(a,"td,th");if(s)return r(e,s,t);var c=d(o,!e);return u(c),t.preventDefault(),!1}function d(e,t){var o=e&&e[t?"lastChild":"firstChild"];return o&&"BR"===o.nodeName?n.dom.getParent(o,"td,th"):o}function u(e){n.selection.setCursorLocation(e,0)}function f(){return y==e.UP||y==e.DOWN}function m(e){var t=e.selection.getNode(),n=e.dom.getParent(t,"tr");return null!==n}function p(e){for(var t=0,n=e;n.previousSibling;)n=n.previousSibling,t+=o(n,"colspan");return t}function g(e,t){var n=0,r=0;return i(e.children,function(e,i){return n+=o(e,"colspan"),r=i,n>t?!1:void 0}),r}function h(e,t,o,i){var r=p(n.dom.getParent(t,"td,th")),a=g(o,r),l=o.childNodes[a],s=d(l,i);u(s||l)}function v(e){var t=n.selection.getNode(),o=n.dom.getParent(t,"td,th"),i=n.dom.getParent(e,"td,th");return o&&o!==i&&b(o,i)}function b(e,t){return n.dom.getParent(e,"TABLE")===n.dom.getParent(t,"TABLE")}var y=t.keyCode;if(f()&&m(n)){var w=n.selection.getNode();setTimeout(function(){v(w)&&r(!t.shiftKey&&y===e.UP,w,t)},0)}}n.on("KeyDown",function(e){t(e)})}function a(){function e(e,t){var n=t.ownerDocument,o=n.createRange(),i;return o.setStartBefore(t),o.setEnd(e.endContainer,e.endOffset),i=n.createElement("body"),i.appendChild(o.cloneContents()),0===i.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi,"-").replace(/<[^>]+>/g,"").length}n.on("KeyDown",function(t){var o,i,r=n.dom;(37==t.keyCode||38==t.keyCode)&&(o=n.selection.getRng(),i=r.getParent(o.startContainer,"table"),i&&n.getBody().firstChild==i&&e(o,i)&&(o=r.createRng(),o.setStartBefore(i),o.setEndBefore(i),n.selection.setRng(o),t.preventDefault()))})}function l(){n.on("KeyDown SetContent VisualAid",function(){var e;for(e=n.getBody().lastChild;e;e=e.previousSibling)if(3==e.nodeType){if(e.nodeValue.length>0)break}else if(1==e.nodeType&&!e.getAttribute("data-mce-bogus"))break;e&&"TABLE"==e.nodeName&&(n.settings.forced_root_block?n.dom.add(n.getBody(),n.settings.forced_root_block,n.settings.forced_root_block_attrs,t.ie&&t.ie<11?" ":'
        '):n.dom.add(n.getBody(),"br",{"data-mce-bogus":"1"}))}),n.on("PreProcess",function(e){var t=e.node.lastChild;t&&("BR"==t.nodeName||1==t.childNodes.length&&("BR"==t.firstChild.nodeName||"\xa0"==t.firstChild.nodeValue))&&t.previousSibling&&"TABLE"==t.previousSibling.nodeName&&n.dom.remove(t)})}function s(){function e(e,t,n,o){var i=3,r=e.dom.getParent(t.startContainer,"TABLE"),a,l,s;return r&&(a=r.parentNode),l=t.startContainer.nodeType==i&&0===t.startOffset&&0===t.endOffset&&o&&("TR"==n.nodeName||n==a),s=("TD"==n.nodeName||"TH"==n.nodeName)&&!o,l||s}function t(){var t=n.selection.getRng(),o=n.selection.getNode(),i=n.dom.getParent(t.startContainer,"TD,TH");if(e(n,t,o,i)){i||(i=o);for(var r=i.lastChild;r.lastChild;)r=r.lastChild;t.setEnd(r,r.nodeValue.length),n.selection.setRng(t)}}n.on("KeyDown",function(){t()}),n.on("MouseDown",function(e){2!=e.button&&t()})}function c(){n.on("keydown",function(t){if((t.keyCode==e.DELETE||t.keyCode==e.BACKSPACE)&&!t.isDefaultPrevented()){var o=n.dom.getParent(n.selection.getStart(),"table");if(o){for(var i=n.dom.select("td,th",o),r=i.length;r--;)if(!n.dom.hasClass(i[r],"mce-item-selected"))return;t.preventDefault(),n.execCommand("mceTableDelete")}}})}c(),t.webkit&&(r(),s()),t.gecko&&(a(),l()),t.ie>10&&(a(),l())}}),o(m,[s,p,c],function(e,t,n){return function(o){function i(){o.getBody().style.webkitUserSelect="",d&&(o.dom.removeClass(o.dom.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),d=!1)}function r(t){var n,i,r=t.target;if(s&&(l||r!=s)&&("TD"==r.nodeName||"TH"==r.nodeName)){i=a.getParent(r,"table"),i==c&&(l||(l=new e(o,i),l.setStartCell(s),o.getBody().style.webkitUserSelect="none"),l.setEndCell(r),d=!0),n=o.selection.getSel();try{n.removeAllRanges?n.removeAllRanges():n.empty()}catch(u){}t.preventDefault()}}var a=o.dom,l,s,c,d=!0;return o.on("MouseDown",function(e){2!=e.button&&(i(),s=a.getParent(e.target,"td,th"),c=a.getParent(s,"table"))}),o.on("mouseover",r),o.on("remove",function(){a.unbind(o.getDoc(),"mouseover",r)}),o.on("MouseUp",function(){function e(e,o){var r=new t(e,e);do{if(3==e.nodeType&&0!==n.trim(e.nodeValue).length)return void(o?i.setStart(e,0):i.setEnd(e,e.nodeValue.length));if("BR"==e.nodeName)return void(o?i.setStartBefore(e):i.setEndBefore(e))}while(e=o?r.next():r.prev())}var i,r=o.selection,d,u,f,m,p;if(s){if(l&&(o.getBody().style.webkitUserSelect=""),d=a.select("td.mce-item-selected,th.mce-item-selected"),d.length>0){i=a.createRng(),f=d[0],p=d[d.length-1],i.setStartBefore(f),i.setEndAfter(f),e(f,1),u=new t(f,a.getParent(d[0],"table"));do if("TD"==f.nodeName||"TH"==f.nodeName){if(!a.hasClass(f,"mce-item-selected"))break;m=f}while(f=u.next());e(m),r.setRng(i)}o.nodeChanged(),s=l=c=null}}),o.on("KeyUp Drop",function(){i(),s=l=c=null}),{clear:i}}}),o(g,[s,u,m,c,p,d,h],function(e,t,n,o,i,r,a){function l(o){function i(e){return e?e.replace(/px$/,""):""}function a(e){return/^[0-9]+$/.test(e)&&(e+="px"),e}function l(e){s("left center right".split(" "),function(t){o.formatter.remove("align"+t,{},e)})}function c(e){s("top middle bottom".split(" "),function(t){o.formatter.remove("valign"+t,{},e)})}function d(){var e=o.dom,t,n,c,d;t=e.getParent(o.selection.getStart(),"table"),d={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),cellspacing:t?e.getAttrib(t,"cellspacing"):"",cellpadding:t?e.getAttrib(t,"cellpadding"):"",border:t?e.getAttrib(t,"border"):"",caption:!!e.select("caption",t)[0]},s("left center right".split(" "),function(e){o.formatter.matchNode(t,"align"+e)&&(d.align=e)}),t||(n={label:"Cols",name:"cols"},c={label:"Rows",name:"rows"}),o.windowManager.open({title:"Table properties",items:{type:"form",layout:"grid",columns:2,data:d,defaults:{type:"textbox",maxWidth:50},items:[n,c,{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell spacing",name:"cellspacing"},{label:"Cell padding",name:"cellpadding"},{label:"Border",name:"border"},{label:"Caption",name:"caption",type:"checkbox"},{label:"Alignment",minWidth:90,name:"align",type:"listbox",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]}]},onsubmit:function(){var n=this.toJSON(),i;o.undoManager.transact(function(){t||(t=g(n.cols||1,n.rows||1)),o.dom.setAttribs(t,{cellspacing:n.cellspacing,cellpadding:n.cellpadding,border:n.border}),o.dom.setStyles(t,{width:a(n.width),height:a(n.height)}),i=e.select("caption",t)[0],i&&!n.caption&&e.remove(i),!i&&n.caption&&(i=e.create("caption"),i.innerHTML=r.ie?"\xa0":'
        ',t.insertBefore(i,t.firstChild)),l(t),n.align&&o.formatter.apply("align"+n.align,{},t),o.focus(),o.addVisual()})}})}function u(e,t){o.windowManager.open({title:"Merge cells",body:[{label:"Cols",name:"cols",type:"textbox",size:10},{label:"Rows",name:"rows",type:"textbox",size:10}],onsubmit:function(){var n=this.toJSON();o.undoManager.transact(function(){e.merge(t,n.cols,n.rows)})}})}function f(){var e=o.dom,t,n,r=[];r=o.dom.select("td.mce-item-selected,th.mce-item-selected"),t=o.dom.getParent(o.selection.getStart(),"td,th"),!r.length&&t&&r.push(t),t=t||r[0],t&&(n={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),scope:e.getAttrib(t,"scope")},n.type=t.nodeName.toLowerCase(),s("left center right".split(" "),function(e){o.formatter.matchNode(t,"align"+e)&&(n.align=e)}),s("top middle bottom".split(" "),function(e){o.formatter.matchNode(t,"valign"+e)&&(n.valign=e)}),o.windowManager.open({title:"Cell properties",items:{type:"form",data:n,layout:"grid",columns:2,defaults:{type:"textbox",maxWidth:50},items:[{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell type",name:"type",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"Cell",value:"td"},{text:"Header cell",value:"th"}]},{label:"Scope",name:"scope",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Row",value:"row"},{text:"Column",value:"col"},{text:"Row group",value:"rowgroup"},{text:"Column group",value:"colgroup"}]},{label:"H Align",name:"align",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"V Align",name:"valign",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Top",value:"top"},{text:"Middle",value:"middle"},{text:"Bottom",value:"bottom"}]}]},onsubmit:function(){var t=this.toJSON();o.undoManager.transact(function(){s(r,function(n){o.dom.setAttrib(n,"scope",t.scope),o.dom.setStyles(n,{width:a(t.width),height:a(t.height)}),t.type&&n.nodeName.toLowerCase()!=t.type&&(n=e.rename(n,t.type)),l(n),t.align&&o.formatter.apply("align"+t.align,{},n),c(n),t.valign&&o.formatter.apply("valign"+t.valign,{},n)}),o.focus()})}}))}function m(){var e=o.dom,t,n,r,c,d=[];t=o.dom.getParent(o.selection.getStart(),"table"),n=o.dom.getParent(o.selection.getStart(),"td,th"),s(t.rows,function(t){s(t.cells,function(o){return e.hasClass(o,"mce-item-selected")||o==n?(d.push(t),!1):void 0})}),r=d[0],r&&(c={height:i(e.getStyle(r,"height")||e.getAttrib(r,"height")),scope:e.getAttrib(r,"scope")},c.type=r.parentNode.nodeName.toLowerCase(),s("left center right".split(" "),function(e){o.formatter.matchNode(r,"align"+e)&&(c.align=e)}),o.windowManager.open({title:"Row properties",items:{type:"form",data:c,columns:2,defaults:{type:"textbox"},items:[{type:"listbox",name:"type",label:"Row type",text:"None",maxWidth:null,values:[{text:"Header",value:"thead"},{text:"Body",value:"tbody"},{text:"Footer",value:"tfoot"}]},{type:"listbox",name:"align",label:"Alignment",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"Height",name:"height"}]},onsubmit:function(){var t=this.toJSON(),n,i,r;o.undoManager.transact(function(){var c=t.type;s(d,function(s){o.dom.setAttrib(s,"scope",t.scope),o.dom.setStyles(s,{height:a(t.height)}),c!=s.parentNode.nodeName.toLowerCase()&&(n=e.getParent(s,"table"),i=s.parentNode,r=e.select(c,n)[0],r||(r=e.create(c),n.firstChild?n.insertBefore(r,n.firstChild):n.appendChild(r)),r.appendChild(s),i.hasChildNodes()||e.remove(i)),l(s),t.align&&o.formatter.apply("align"+t.align,{},s)}),o.focus()})}}))}function p(e){return function(){o.execCommand(e)}}function g(e,t){var n,i,a;for(a='',n=0;t>n;n++){for(a+="",i=0;e>i;i++)a+="";a+=""}a+="
        "+(r.ie?" ":"
        ")+"
        ",o.insertContent(a);var l=o.dom.get("__mce");return o.dom.setAttrib(l,"id",null),l}function h(e,t){function n(){e.disabled(!o.dom.getParent(o.selection.getStart(),t)),o.selection.selectorChanged(t,function(t){e.disabled(!t)})}o.initialized?n():o.on("init",n)}function v(){h(this,"table")}function b(){h(this,"td,th")}function y(){var e="";e='';for(var t=0;10>t;t++){e+="";for(var n=0;10>n;n++)e+='';e+=""}return e+="
        ",e+='

        '}function w(e,t,n){var i=n.getEl().getElementsByTagName("table")[0],r,a,l,s,c,d=n.isRtl()||"tl-tr"==n.parent().rel;for(i.nextSibling.innerHTML=e+1+" x "+(t+1),d&&(e=9-e),a=0;10>a;a++)for(r=0;10>r;r++)s=i.rows[a].childNodes[r].firstChild,c=(d?r>=e:e>=r)&&t>=a,o.dom.toggleClass(s,"mce-active",c),c&&(l=s);return l.parentNode}var x,C,P=this;o.settings.table_grid===!1?o.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",onclick:d}):o.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",ariaHideMenu:!0,onclick:function(e){e.aria&&(this.parent().hideAll(),e.stopImmediatePropagation(),d())},onshow:function(){w(0,0,this.menu.items()[0])},onhide:function(){var e=this.menu.items()[0].getEl().getElementsByTagName("a");o.dom.removeClass(e,"mce-active"),o.dom.addClass(e[0],"mce-active")},menu:[{type:"container",html:y(),onPostRender:function(){this.lastX=this.lastY=0},onmousemove:function(e){var t=e.target,n,o;"A"==t.tagName.toUpperCase()&&(n=parseInt(t.getAttribute("data-mce-x"),10),o=parseInt(t.getAttribute("data-mce-y"),10),(this.isRtl()||"tl-tr"==this.parent().rel)&&(n=9-n),(n!==this.lastX||o!==this.lastY)&&(w(n,o,e.control),this.lastX=n,this.lastY=o))},onkeydown:function(e){var t=this.lastX,n=this.lastY,o;switch(e.keyCode){case 37:t>0&&(t--,o=!0);break;case 39:o=!0,9>t&&t++;break;case 38:o=!0,n>0&&n--;break;case 40:o=!0,9>n&&n++}o&&(e.preventDefault(),e.stopPropagation(),w(t,n,e.control).focus(),this.lastX=t,this.lastY=n)},onclick:function(e){"A"==e.target.tagName.toUpperCase()&&(e.preventDefault(),e.stopPropagation(),this.parent().cancel(),g(this.lastX+1,this.lastY+1))}}]}),o.addMenuItem("tableprops",{text:"Table properties",context:"table",onPostRender:v,onclick:d}),o.addMenuItem("deletetable",{text:"Delete table",context:"table",onPostRender:v,cmd:"mceTableDelete"}),o.addMenuItem("cell",{separator:"before",text:"Cell",context:"table",menu:[{text:"Cell properties",onclick:p("mceTableCellProps"),onPostRender:b},{text:"Merge cells",onclick:p("mceTableMergeCells"),onPostRender:b},{text:"Split cell",onclick:p("mceTableSplitCells"),onPostRender:b}]}),o.addMenuItem("row",{text:"Row",context:"table",menu:[{text:"Insert row before",onclick:p("mceTableInsertRowBefore"),onPostRender:b},{text:"Insert row after",onclick:p("mceTableInsertRowAfter"),onPostRender:b},{text:"Delete row",onclick:p("mceTableDeleteRow"),onPostRender:b},{text:"Row properties",onclick:p("mceTableRowProps"),onPostRender:b},{text:"-"},{text:"Cut row",onclick:p("mceTableCutRow"),onPostRender:b},{text:"Copy row",onclick:p("mceTableCopyRow"),onPostRender:b},{text:"Paste row before",onclick:p("mceTablePasteRowBefore"),onPostRender:b},{text:"Paste row after",onclick:p("mceTablePasteRowAfter"),onPostRender:b}]}),o.addMenuItem("column",{text:"Column",context:"table",menu:[{text:"Insert column before",onclick:p("mceTableInsertColBefore"),onPostRender:b},{text:"Insert column after",onclick:p("mceTableInsertColAfter"),onPostRender:b},{text:"Delete column",onclick:p("mceTableDeleteCol"),onPostRender:b}]});var S=[];s("inserttable tableprops deletetable | cell row column".split(" "),function(e){S.push("|"==e?{text:"-"}:o.menuItems[e])}),o.addButton("table",{type:"menubutton",title:"Table",menu:S}),r.isIE||o.on("click",function(e){e=e.target,"TABLE"===e.nodeName&&(o.selection.select(e),o.nodeChanged())}),P.quirks=new t(o),o.on("Init",function(){x=o.windowManager,P.cellSelection=new n(o)}),s({mceTableSplitCells:function(e){e.split()},mceTableMergeCells:function(e){var t,n,i;i=o.dom.getParent(o.selection.getStart(),"th,td"),i&&(t=i.rowSpan,n=i.colSpan),o.dom.select("td.mce-item-selected,th.mce-item-selected").length?e.merge():u(e,i)},mceTableInsertRowBefore:function(e){e.insertRow(!0)},mceTableInsertRowAfter:function(e){e.insertRow()},mceTableInsertColBefore:function(e){e.insertCol(!0)},mceTableInsertColAfter:function(e){e.insertCol()},mceTableDeleteCol:function(e){e.deleteCols()},mceTableDeleteRow:function(e){e.deleteRows()},mceTableCutRow:function(e){C=e.cutRows()},mceTableCopyRow:function(e){C=e.copyRows()},mceTablePasteRowBefore:function(e){e.pasteRows(C,!0)},mceTablePasteRowAfter:function(e){e.pasteRows(C)},mceTableDelete:function(e){e.deleteTable()}},function(t,n){o.addCommand(n,function(){var n=new e(o);n&&(t(n),o.execCommand("mceRepaint"),P.cellSelection.clear())})}),s({mceInsertTable:function(){d()},mceTableRowProps:m,mceTableCellProps:f},function(e,t){o.addCommand(t,function(t,n){e(n)})})}var s=o.each;a.add("table",l)}),a([s,u,m,g])}(this);tinymce.PluginManager.add("template",function(e){function t(t){return function(){var a=e.settings.templates;"string"==typeof a?tinymce.util.XHR.send({url:a,success:function(e){t(tinymce.util.JSON.parse(e))}}):t(a)}}function a(t){function a(t){function a(t){if(-1==t.indexOf("")){var a="";tinymce.each(e.contentCSS,function(t){a+=''}),t=""+a+""+t+""}t=r(t,"template_preview_replace_values");var l=n.find("iframe")[0].getEl().contentWindow.document;l.open(),l.write(t),l.close()}var c=t.control.value();c.url?tinymce.util.XHR.send({url:c.url,success:function(e){l=e,a(l)}}):(l=c.content,a(l)),n.find("#description")[0].text(t.control.value().description)}var n,l,i=[];return t&&0!==t.length?(tinymce.each(t,function(e){i.push({selected:!i.length,text:e.title,value:{url:e.url,content:e.content,description:e.description}})}),n=e.windowManager.open({title:"Insert template",layout:"flex",direction:"column",align:"stretch",padding:15,spacing:10,items:[{type:"form",flex:0,padding:0,items:[{type:"container",label:"Templates",items:{type:"listbox",label:"Templates",name:"template",values:i,onselect:a}}]},{type:"label",name:"description",label:"Description",text:" "},{type:"iframe",flex:1,border:1}],onsubmit:function(){c(!1,l)},width:e.getParam("template_popup_width",600),height:e.getParam("template_popup_height",500)}),void n.find("listbox")[0].fire("select")):void e.windowManager.alert("No templates defined")}function n(t,a){function n(e,t){if(e=""+e,e.length0&&(o=p.create("div",null),o.appendChild(s[0].cloneNode(!0))),i(p.select("*",o),function(t){c(t,e.getParam("template_cdate_classes","cdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_cdate_format",e.getLang("template.cdate_format")))),c(t,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_mdate_format",e.getLang("template.mdate_format")))),c(t,e.getParam("template_selected_content_classes","selcontent").replace(/\s+/g,"|"))&&(t.innerHTML=m)}),l(o),e.execCommand("mceInsertContent",!1,o.innerHTML),e.addVisual()}var i=tinymce.each;e.addCommand("mceInsertTemplate",c),e.addButton("template",{title:"Insert template",onclick:t(a)}),e.addMenuItem("template",{text:"Insert template",onclick:t(a),context:"insert"}),e.on("PreProcess",function(t){var a=e.dom;i(a.select("div",t.node),function(t){a.hasClass(t,"mceTmpl")&&(i(a.select("*",t),function(t){a.hasClass(t,e.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(t.innerHTML=n(e.getParam("template_mdate_format",e.getLang("template.mdate_format"))))}),l(t))})})});tinymce.PluginManager.add("textcolor",function(e){function t(){var t,o,l=[];for(o=e.settings.textcolor_map||["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Red violet","C0C0C0","Silver","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum","FFFFFF","White"],t=0;t',a=o.length-1,c=e.settings.textcolor_rows||5,i=e.settings.textcolor_cols||8,F=0;c>F;F++){for(r+="",n=0;i>n;n++)d=F*i+n,d>a?r+="":(l=o[d],r+='
        ');r+=""}return r+=""}function l(t){var o,l=this.parent();(o=t.target.getAttribute("data-mce-color"))&&(this.lastId&&document.getElementById(this.lastId).setAttribute("aria-selected",!1),t.target.setAttribute("aria-selected",!0),this.lastId=t.target.id,l.hidePanel(),o="#"+o,l.color(o),e.execCommand(l.settings.selectcmd,!1,o))}function r(){var t=this;t._color&&e.execCommand(t.settings.selectcmd,!1,t._color)}e.addButton("forecolor",{type:"colorbutton",tooltip:"Text color",selectcmd:"ForeColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:l},onclick:r}),e.addButton("backcolor",{type:"colorbutton",tooltip:"Background color",selectcmd:"HiliteColor",panel:{role:"application",ariaRemember:!0,html:o,onclick:l},onclick:r})});tinymce.PluginManager.add("visualblocks",function(e,s){function o(){var s=this;s.active(a),e.on("VisualBlocks",function(){s.active(e.dom.hasClass(e.getBody(),"mce-visualblocks"))})}var l,t,a;window.NodeList&&(e.addCommand("mceVisualBlocks",function(){var o,c=e.dom;l||(l=c.uniqueId(),o=c.create("link",{id:l,rel:"stylesheet",href:s+"/css/visualblocks.css"}),e.getDoc().getElementsByTagName("head")[0].appendChild(o)),e.on("PreviewFormats AfterPreviewFormats",function(s){a&&c.toggleClass(e.getBody(),"mce-visualblocks","afterpreviewformats"==s.type)}),c.toggleClass(e.getBody(),"mce-visualblocks"),a=e.dom.hasClass(e.getBody(),"mce-visualblocks"),t&&t.active(c.hasClass(e.getBody(),"mce-visualblocks")),e.fire("VisualBlocks")}),e.addButton("visualblocks",{title:"Show blocks",cmd:"mceVisualBlocks",onPostRender:o}),e.addMenuItem("visualblocks",{text:"Show blocks",cmd:"mceVisualBlocks",onPostRender:o,selectable:!0,context:"view",prependToContext:!0}),e.on("init",function(){e.settings.visualblocks_default_state&&e.execCommand("mceVisualBlocks",!1,null,{skip_focus:!0})}),e.on("remove",function(){e.dom.removeClass(e.getBody(),"mce-visualblocks")}))});tinymce.PluginManager.add("visualchars",function(e){function a(a){var t,s,i,r,c,d,l=e.getBody(),m=e.selection;if(n=!n,o.state=n,e.fire("VisualChars",{state:n}),a&&(d=m.getBookmark()),n)for(s=[],tinymce.walk(l,function(e){3==e.nodeType&&e.nodeValue&&-1!=e.nodeValue.indexOf(" ")&&s.push(e)},"childNodes"),i=0;i$1'),c=e.dom.create("div",null,r);t=c.lastChild;)e.dom.insertAfter(t,s[i]);e.dom.remove(s[i])}else for(s=e.dom.select("span.mce-nbsp",l),i=s.length-1;i>=0;i--)e.dom.remove(s[i],1);m.moveToBookmark(d)}function t(){var a=this;e.on("VisualChars",function(e){a.active(e.state)})}var n,o=this;e.addCommand("mceVisualChars",a),e.addButton("visualchars",{title:"Show invisible characters",cmd:"mceVisualChars",onPostRender:t}),e.addMenuItem("visualchars",{text:"Show invisible characters",cmd:"mceVisualChars",onPostRender:t,selectable:!0,context:"view",prependToContext:!0}),e.on("beforegetcontent",function(e){n&&"raw"!=e.format&&!e.draft&&(n=!0,a(!1))})});tinymce.PluginManager.add("wordcount",function(e){function t(){e.theme.panel.find("#wordcount").text(["Words: {0}",a.getCount()])}var n,o,a=this;n=e.getParam("wordcount_countregex",/[\w\u2019\x27\-\u00C0-\u1FFF]+/g),o=e.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\x27\x22_+=\\\/\-]*/g),e.on("init",function(){var n=e.theme.panel&&e.theme.panel.find("#statusbar")[0];n&&window.setTimeout(function(){n.insert({type:"label",name:"wordcount",text:["Words: {0}",a.getCount()],classes:"wordcount",disabled:e.settings.readonly},0),e.on("setcontent beforeaddundo",t),e.on("keyup",function(e){32==e.keyCode&&t()})},0)}),a.getCount=function(){var t=e.getContent({format:"raw"}),a=0;if(t){t=t.replace(/\.\.\./g," "),t=t.replace(/<.[^<>]*?>/g," ").replace(/ | /gi," "),t=t.replace(/(\w+)(&#?[a-z0-9]+;)+(\w+)/i,"$1$3").replace(/&.+?;/g," "),t=t.replace(o,"");var r=t.match(n);r&&(a=r.length)}return a}});tinymce.ThemeManager.add("modern",function(e){function t(){function t(t){var n,o=[];if(t)return d(t.split(/[ ,]/),function(t){function i(){var i=e.selection;"bullist"==r&&i.selectorChanged("ul > li",function(e,i){for(var n,o=i.parents.length;o--&&(n=i.parents[o].nodeName,"OL"!=n&&"UL"!=n););t.active(e&&"UL"==n)}),"numlist"==r&&i.selectorChanged("ol > li",function(e,i){for(var n,o=i.parents.length;o--&&(n=i.parents[o].nodeName,"OL"!=n&&"UL"!=n););t.active(e&&"OL"==n)}),t.settings.stateSelector&&i.selectorChanged(t.settings.stateSelector,function(e){t.active(e)},!0),t.settings.disabledStateSelector&&i.selectorChanged(t.settings.disabledStateSelector,function(e){t.disabled(e)})}var r;"|"==t?n=null:c.has(t)?(t={type:t},u.toolbar_items_size&&(t.size=u.toolbar_items_size),o.push(t),n=null):(n||(n={type:"buttongroup",items:[]},o.push(n)),e.buttons[t]&&(r=t,t=e.buttons[r],"function"==typeof t&&(t=t()),t.type=t.type||"button",u.toolbar_items_size&&(t.size=u.toolbar_items_size),t=c.create(t),n.items.push(t),e.initialized?i():e.on("init",i)))}),i.push({type:"toolbar",layout:"flow",items:o}),!0}var i=[];if(tinymce.isArray(u.toolbar)){if(0===u.toolbar.length)return;tinymce.each(u.toolbar,function(e,t){u["toolbar"+(t+1)]=e}),delete u.toolbar}for(var n=1;10>n&&t(u["toolbar"+n]);n++);return i.length||u.toolbar===!1||t(u.toolbar||f),i.length?{type:"panel",layout:"stack",classes:"toolbar-grp",ariaRoot:!0,ariaRemember:!0,items:i}:void 0}function i(){function t(t){var i;return"|"==t?{text:"|"}:i=e.menuItems[t]}function i(i){var n,o,r,a,s;if(s=tinymce.makeMap((u.removed_menuitems||"").split(/[ ,]/)),u.menu?(o=u.menu[i],a=!0):o=h[i],o){n={text:o.title},r=[],d((o.items||"").split(/[ ,]/),function(e){var i=t(e);i&&!s[e]&&r.push(t(e))}),a||d(e.menuItems,function(e){e.context==i&&("before"==e.separator&&r.push({text:"|"}),e.prependToContext?r.unshift(e):r.push(e),"after"==e.separator&&r.push({text:"|"}))});for(var l=0;l
        This is a contentEditable area
        + +
        diff --git a/lib/test/data/rich_text.html b/lib/test/data/rich_text.html index 8c9a073..a42e43a 100644 --- a/lib/test/data/rich_text.html +++ b/lib/test/data/rich_text.html @@ -23,7 +23,8 @@ window.onload = function () { // Super dumb browser detection - var isIE = window.navigator.userAgent.search('MSIE') != -1; + var isIE = window.navigator.userAgent.search('MSIE') != -1 + || window.navigator.userAgent.search('Trident') != -1; var editFrame = document.getElementById('editFrame').contentWindow; setKeypressHandler(editFrame.document, printEventData); diff --git a/lib/test/data/screen/screen_frame1.html b/lib/test/data/screen/screen_frame1.html index d50c21d..35b03ae 100644 --- a/lib/test/data/screen/screen_frame1.html +++ b/lib/test/data/screen/screen_frame1.html @@ -1,6 +1,6 @@ -screen test +screen frame1 diff --git a/lib/test/data/screen/screen_frame2.html b/lib/test/data/screen/screen_frame2.html index b66cd70..e6e17e6 100644 --- a/lib/test/data/screen/screen_frame2.html +++ b/lib/test/data/screen/screen_frame2.html @@ -1,6 +1,6 @@ -screen test +screen frame2 diff --git a/lib/test/data/slowLoadingResourcePage.html b/lib/test/data/slowLoadingResourcePage.html index e05f954..02796c3 100644 --- a/lib/test/data/slowLoadingResourcePage.html +++ b/lib/test/data/slowLoadingResourcePage.html @@ -7,6 +7,6 @@ too long to respond. Normally these things are loaded in an iframe, which is what we're doing here.

        - + - \ No newline at end of file + diff --git a/lib/test/data/tinymce.html b/lib/test/data/tinymce.html new file mode 100644 index 0000000..067b66c --- /dev/null +++ b/lib/test/data/tinymce.html @@ -0,0 +1,10 @@ + + + + TinyMCE + + + + + + \ No newline at end of file diff --git a/lib/test/data/transparentUpload.html b/lib/test/data/transparentUpload.html new file mode 100644 index 0000000..87b02bf --- /dev/null +++ b/lib/test/data/transparentUpload.html @@ -0,0 +1,70 @@ + + + + Upload Form + + + + +
        +
        +
        + Upload + +
        +
        +
        + + +
        + + diff --git a/lib/test/fileserver.js b/lib/test/fileserver.js index a4165df..6a86947 100644 --- a/lib/test/fileserver.js +++ b/lib/test/fileserver.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -20,18 +22,21 @@ var fs = require('fs'), path = require('path'), url = require('url'); +var express = require('express'); +var multer = require('multer'); +var serveIndex = require('serve-index'); + var Server = require('./httpserver').Server, resources = require('./resources'), promise = require('../..').promise, isDevMode = require('../../_base').isDevMode(), string = require('../../_base').require('goog.string'); -var WEB_ROOT = isDevMode ? '/common/src/web' : '/common'; +var WEB_ROOT = '/common'; var JS_ROOT = '/javascript'; -var baseDirectory = resources.locate('.'), - server = new Server(onRequest); - +var baseDirectory = resources.locate(isDevMode ? 'common/src/web' : '.'); +var jsDirectory = resources.locate(isDevMode ? 'javascript' : '..'); var Pages = (function() { var pages = {}; @@ -58,6 +63,7 @@ var Pages = (function() { addPage('documentWrite', 'document_write_in_onload.html'); addPage('dynamicallyModifiedPage', 'dynamicallyModifiedPage.html'); addPage('dynamicPage', 'dynamic.html'); + addPage('echoPage', 'echo'); addPage('errorsPage', 'errors.html'); addPage('xhtmlFormPage', 'xhtmlFormPage.xhtml'); addPage('formPage', 'formPage.html'); @@ -102,93 +108,41 @@ var Pages = (function() { var Path = { - BASIC_AUTH: path.join(WEB_ROOT, 'basicAuth'), - MANIFEST: path.join(WEB_ROOT, 'manifest'), - REDIRECT: path.join(WEB_ROOT, 'redirect'), - PAGE: path.join(WEB_ROOT, 'page'), - SLEEP: path.join(WEB_ROOT, 'sleep'), - UPLOAD: path.join(WEB_ROOT, 'upload') + BASIC_AUTH: WEB_ROOT + '/basicAuth', + ECHO: WEB_ROOT + '/echo', + GENERATED: WEB_ROOT + '/generated', + MANIFEST: WEB_ROOT + '/manifest', + REDIRECT: WEB_ROOT + '/redirect', + PAGE: WEB_ROOT + '/page', + SLEEP: WEB_ROOT + '/sleep', + UPLOAD: WEB_ROOT + '/upload' }; - -/** - * HTTP request handler. - * @param {!http.ServerRequest} request The request object. - * @param {!http.ServerResponse} response The response object. - */ -function onRequest(request, response) { - if (request.method !== 'GET' && request.method !== 'HEAD') { - response.writeHead(405, {'Allowed': 'GET,HEAD'}); - return response.end(); - } - - var pathname = path.resolve(url.parse(request.url).pathname); - if (pathname === '/') { - return sendIndex(request, response); - } - - if (pathname === '/favicon.ico') { - response.writeHead(204); - return response.end(); - } - - switch (pathname) { - case Path.BASIC_AUTH: return sendBasicAuth(response); - case Path.MANIFEST: return sendManifest(response); - case Path.PAGE: return sendInifinitePage(request, response); - case Path.REDIRECT: return redirectToResultPage(response); - case Path.SLEEP: return sendDelayedResponse(request, response); - case Path.UPLOAD: return sendUpload(response); - } - - if (string.startsWith(pathname, Path.PAGE + '/')) { - return sendInifinitePage(request, response); - } - - if ((string.startsWith(pathname, WEB_ROOT) || - string.startsWith(pathname, JS_ROOT)) && - string.endsWith(pathname, '.appcache')) { - return sendManifest(response); - } - - if (string.startsWith(pathname, WEB_ROOT)) { - if (!isDevMode) { - pathname = pathname.substring(WEB_ROOT.length); - } - } else if (string.startsWith(pathname, JS_ROOT)) { - if (!isDevMode) { - sendSimpleError(response, 404, request.url); - return; - } - pathname = pathname.substring(JS_ROOT); - } - - try { - var fullPath = resources.locate(pathname); - } catch (ex) { - fullPath = ''; - } - - if (fullPath.lastIndexOf(baseDirectory, 0) == -1) { - sendSimpleError(response, 404, request.url); - return; - } - - fs.stat(fullPath, function(err, stats) { - if (err) { - sendIOError(request, response, err); - } else if (stats.isDirectory()) { - sendDirectoryListing(request, response, fullPath); - } else if (stats.isFile()) { - sendFile(request, response, fullPath); - } else { - sendSimpleError(response, 404, request.url); - } - }); +var app = express(); + +app.get('/', sendIndex) +.get('/favicon.ico', function(req, res) { + res.writeHead(204); + res.end(); +}) +.use(JS_ROOT, serveIndex(jsDirectory), express.static(jsDirectory)) +.post(Path.UPLOAD, handleUpload) +.use(WEB_ROOT, serveIndex(baseDirectory), express.static(baseDirectory)) +.get(Path.ECHO, sendEcho) +.get(Path.PAGE, sendInifinitePage) +.get(Path.PAGE + '/*', sendInifinitePage) +.get(Path.REDIRECT, redirectToResultPage) +.get(Path.SLEEP, sendDelayedResponse) + +if (isDevMode) { + var closureDir = resources.locate('third_party/closure/goog'); + app.use('/third_party/closure/goog', + serveIndex(closureDir), express.static(closureDir)); } +var server = new Server(app); -function redirectToResultPage(response) { +function redirectToResultPage(_, response) { response.writeHead(303, { Location: Pages.resultPage }); @@ -197,23 +151,21 @@ function redirectToResultPage(response) { function sendInifinitePage(request, response) { - setTimeout(function() { - var pathname = url.parse(request.url).pathname; - var lastIndex = pathname.lastIndexOf('/'); - var pageNumber = - (lastIndex == -1 ? 'Unknown' : pathname.substring(lastIndex + 1)); - var body = [ - '', - 'Page', pageNumber, '', - 'Page number ', pageNumber, '', - '

        top' - ].join(''); - response.writeHead(200, { - 'Content-Length': Buffer.byteLength(body, 'utf8'), - 'Content-Type': 'text/html; charset=utf-8' - }); - response.end(body); - }, 500); + var pathname = url.parse(request.url).pathname; + var lastIndex = pathname.lastIndexOf('/'); + var pageNumber = + (lastIndex == -1 ? 'Unknown' : pathname.substring(lastIndex + 1)); + var body = [ + '', + 'Page', pageNumber, '', + 'Page number ', pageNumber, '', + '

        top' + ].join(''); + response.writeHead(200, { + 'Content-Length': Buffer.byteLength(body, 'utf8'), + 'Content-Type': 'text/html; charset=utf-8' + }); + response.end(body); } @@ -222,7 +174,7 @@ function sendDelayedResponse(request, response) { var query = url.parse(request.url).query || ''; var match = query.match(/\btime=(\d+)/); if (match) { - duration = parseInt(match[1]); + duration = parseInt(match[1], 10); } setTimeout(function() { @@ -243,74 +195,36 @@ function sendDelayedResponse(request, response) { } -/** - * Sends an error in response to an I/O operation. - * @param {!http.ServerRequest} request The request object. - * @param {!http.ServerResponse} response The response object. - * @param {!Error} err The I/O error. - */ -function sendIOError(request, response, err) { - var code = 500; - if (err.code === 'ENOENT') { - code = 404; - } else if (err.code === 'EACCES') { - code = 403; - } - sendSimpleError(response, code, request.url); -} - - -/** - * Sends a simple error message to the client and instructs it to close the - * connection. - * @param {!http.ServerResponse} response The response to populate. - * @param {number} code The numeric HTTP code to send. - * @param {string} message The error message. - */ -function sendSimpleError(response, code, message) { - response.writeHead(code, { - 'Content-Type': 'text/html; charset=utf-8', - 'Connection': 'close' - }); - response.end( - '

        ' + code + ' ' + http.STATUS_CODES[code] + - '


        ' + message); +function handleUpload(request, response, next) { + multer({ + inMemory: true, + onFileUploadComplete: function(file) { + response.writeHead(200); + response.write(file.buffer); + response.end(''); + } + })(request, response, function() {}); } -var MimeType = { - 'css': 'text/css', - 'gif': 'image/gif', - 'html': 'text/html', - 'js': 'application/javascript', - 'png': 'image/png', - 'svg': 'image/svg+xml', - 'txt': 'text/plain', - 'xhtml': 'application/xhtml+xml', - 'xsl': 'application/xml', - 'xml': 'application/xml' -}; - - -/** - * Responds to a request for an individual file. - * @param {!http.ServerRequest} request The request object. - * @param {!http.ServerResponse} response The response object. - * @param {string} filePath Path to the file to return. - */ -function sendFile(request, response, filePath) { - fs.readFile(filePath, function(err, buffer) { - if (err) { - sendIOError(request, response, err); - return; - } - var index = filePath.lastIndexOf('.'); - var type = MimeType[index < 0 ? '' : filePath.substring(index + 1)]; - var headers = {'Content-Length': buffer.length}; - if (type) headers['Content-Type'] = type; - response.writeHead(200, headers); - response.end(buffer); +function sendEcho(request, response) { + var body = [ + '', + 'Echo', + '
        ', + request.method, ' ', request.url, ' ', 'HTTP/', request.httpVersion, + '
        ' + ]; + for (var name in request.headers) { + body.push('
        ', + name, ': ', request.headers[name], '
        '); + } + body = body.join(''); + response.writeHead(200, { + 'Content-Length': Buffer.byteLength(body, 'utf8'), + 'Content-Type': 'text/html; charset=utf-8' }); + response.end(body); } @@ -350,69 +264,6 @@ function sendIndex(request, response) { } -/** - * Responds to a request for a directory listing. - * @param {!http.ServerRequest} request The request object. - * @param {!http.ServerResponse} response The response object. - * @param {string} dirPath Path to the directory to generate a listing for. - */ -function sendDirectoryListing(request, response, dirPath) { - var pathname = url.parse(request.url).pathname; - - var host = request.headers.host; - if (!host) { - host = server.host(); - } - - var requestUrl = ['http://' + host + pathname].join(''); - if (requestUrl[requestUrl.length - 1] !== '/') { - response.writeHead(303, {'Location': requestUrl + '/'}); - return response.end(); - } - - fs.readdir(dirPath, function(err, files) { - if (err) { - sendIOError(request, response, err); - return; - } - - var data = ['

        ', pathname, '


          ']; - if (pathname !== '/') { - data.push(createListEntry('../')); - } - processNextFile(); - - function processNextFile() { - var file = files.shift(); - if (file) { - fs.stat(path.join(dirPath, file), function(err, stats) { - if (err) { - sendIOError(request, response, err); - return; - } - - data.push(createListEntry( - stats.isDirectory() ? file + '/' : file)); - processNextFile(); - }); - } else { - data = new Buffer(data.join(''), 'utf-8'); - response.writeHead(200, { - 'Content-Type': 'text/html; charset=utf-8', - 'Content-Length': data.length - }); - response.end(data); - } - } - - function createListEntry(path) { - var url = requestUrl + path; - return ['
        • ', path, ''].join(''); - } - }); -} - - // PUBLIC application @@ -451,7 +302,11 @@ exports.url = server.url.bind(server); * @throws {Error} If the server is not running. */ exports.whereIs = function(filePath) { - return server.url(path.join(WEB_ROOT, filePath)); + filePath = filePath.replace(/\\/g, '/'); + if (!string.startsWith(filePath, '/')) { + filePath = '/' + filePath; + } + return server.url(WEB_ROOT + filePath); }; diff --git a/lib/test/httpserver.js b/lib/test/httpserver.js index e75298e..55b1255 100644 --- a/lib/test/httpserver.js +++ b/lib/test/httpserver.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -19,7 +21,8 @@ var assert = require('assert'), http = require('http'), url = require('url'); -var portprober = require('../../net/portprober'), +var net = require('../../net'), + portprober = require('../../net/portprober'), promise = require('../..').promise; @@ -51,6 +54,8 @@ var Server = function(requestHandler) { * with the server host when it has fully started. */ this.start = function(opt_port) { + assert(typeof opt_port !== 'function', + "start invoked with function, not port (mocha callback)?"); var port = opt_port || portprober.findFreePort('localhost'); return promise.when(port, function(port) { return promise.checkedNodeCall( @@ -88,8 +93,8 @@ var Server = function(requestHandler) { * @throws {Error} If the server is not running. */ this.host = function() { - var addr = this.address(); - return addr.address + ':' + addr.port; + return net.getLoopbackAddress() + ':' + + this.address().port; }; /** @@ -103,7 +108,7 @@ var Server = function(requestHandler) { var pathname = opt_pathname || ''; return url.format({ protocol: 'http', - hostname: addr.address, + hostname: net.getLoopbackAddress(), port: addr.port, pathname: pathname }); diff --git a/lib/test/index.js b/lib/test/index.js index f328bee..2693a96 100644 --- a/lib/test/index.js +++ b/lib/test/index.js @@ -1,74 +1,102 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 assert = require('assert'); -var webdriver = require('../..'), +var build = require('./build'), + webdriver = require('../..'), flow = webdriver.promise.controlFlow(), + _base = require('../../_base'), + remote = require('../../remote'), testing = require('../../testing'), - fileserver = require('./fileserver'), - seleniumserver = require('./seleniumserver'); - - -var Browser = { - ANDROID: 'android', - CHROME: 'chrome', - IE: 'internet explorer', - // Shorthand for IPAD && IPHONE when using the browsers predciate. - IOS: 'iOS', - IPAD: 'iPad', - IPHONE: 'iPhone', - FIREFOX: 'firefox', - OPERA: 'opera', - PHANTOMJS: 'phantomjs', - SAFARI: 'safari', - - // Browsers that should always be tested via the java Selenium server. - REMOTE_CHROME: 'remote.chrome', - REMOTE_PHANTOMJS: 'remote.phantomjs' -}; + fileserver = require('./fileserver'); /** * Browsers with native support. - * @type {!Array.} + * @type {!Array.} */ var NATIVE_BROWSERS = [ - Browser.CHROME, - Browser.PHANTOMJS + webdriver.Browser.CHROME, + webdriver.Browser.FIREFOX, + webdriver.Browser.IE, + webdriver.Browser.OPERA, + webdriver.Browser.PHANTOM_JS, + webdriver.Browser.SAFARI ]; +var serverJar = process.env['SELENIUM_SERVER_JAR']; +var remoteUrl = process.env['SELENIUM_REMOTE_URL']; +var useLoopback = process.env['SELENIUM_USE_LOOP_BACK'] == '1'; +var startServer = !!serverJar && !remoteUrl; +var nativeRun = !serverJar && !remoteUrl; + + var browsersToTest = (function() { - var browsers = process.env['SELENIUM_BROWSER'] || Browser.CHROME; - browsers = browsers.split(','); + var permitRemoteBrowsers = !!remoteUrl || !!serverJar; + var permitUnknownBrowsers = !nativeRun; + var browsers = process.env['SELENIUM_BROWSER'] || webdriver.Browser.FIREFOX; + + browsers = browsers.split(',').map(function(browser) { + var parts = browser.split(/:/); + if (parts[0] === 'ie') { + parts[0] = webdriver.Browser.IE; + } + return parts.join(':'); + }); browsers.forEach(function(browser) { - if (browser === Browser.IOS) { - throw Error('Invalid browser name: ' + browser); + var parts = browser.split(/:/, 3); + if (parts[0] === 'ie') { + parts[0] = webdriver.Browser.IE; + } + + if (NATIVE_BROWSERS.indexOf(parts[0]) == -1 && !permitRemoteBrowsers) { + throw Error('Browser ' + parts[0] + ' requires a WebDriver server and ' + + 'neither the SELENIUM_REMOTE_URL nor the SELENIUM_SERVER_JAR ' + + 'environment variables have been set.'); } - for (var name in Browser) { - if (Browser.hasOwnProperty(name) && Browser[name] === browser) { - return; + var recognized = false; + for (var prop in webdriver.Browser) { + if (webdriver.Browser.hasOwnProperty(prop) && + webdriver.Browser[prop] === parts[0]) { + recognized = true; + break; } } - throw Error('Unrecognized browser: ' + browser); + if (!recognized && !permitUnknownBrowsers) { + throw Error('Unrecognized browser: ' + browser); + } }); + + console.log('Running tests against [' + browsers.join(',') + ']'); + if (remoteUrl) { + console.log('Using remote server ' + remoteUrl); + } else if (serverJar) { + console.log('Using standalone Selenium server ' + serverJar); + if (useLoopback) { + console.log('Running tests using loopback address') + } + } + return browsers; })(); @@ -81,10 +109,7 @@ var browsersToTest = (function() { */ function browsers(currentBrowser, browsersToIgnore) { return function() { - var checkIos = - currentBrowser === Browser.IPAD || currentBrowser === Browser.IPHONE; - return browsersToIgnore.indexOf(currentBrowser) != -1 || - (checkIos && browsersToIgnore.indexOf(Browser.IOS) != -1); + return browsersToIgnore.indexOf(currentBrowser) != -1; }; } @@ -96,80 +121,38 @@ function browsers(currentBrowser, browsersToIgnore) { */ function TestEnvironment(browserName, server) { var name = browserName; - if (name.lastIndexOf('remote.', 0) == 0) { - name = name.substring('remote.'.length); - } - - var autoCreate = true; - this.__defineGetter__( - 'autoCreateDriver', function() { return autoCreate; }); - this.__defineSetter__( - 'autoCreateDriver', function(auto) { autoCreate = auto; }); - this.__defineGetter__('browser', function() { return name; }); + this.currentBrowser = function() { + return browserName; + }; - var driver; - this.__defineGetter__('driver', function() { return driver; }); + this.isRemote = function() { + return server || remoteUrl; + }; this.browsers = function(var_args) { var browsersToIgnore = Array.prototype.slice.apply(arguments, [0]); - var remoteVariants = []; - browsersToIgnore.forEach(function(browser) { - if (browser.lastIndexOf('remote.', 0) === 0) { - remoteVariants.push(browser.substring('remote.'.length)); - } - }); - browsersToIgnore = browsersToIgnore.concat(remoteVariants); return browsers(browserName, browsersToIgnore); }; this.builder = function() { - assert.ok(!driver, 'Can only have one driver at a time'); var builder = new webdriver.Builder(); var realBuild = builder.build; builder.build = function() { - builder.getCapabilities(). - set(webdriver.Capability.BROWSER_NAME, name); - + var parts = browserName.split(/:/, 3); + builder.forBrowser(parts[0], parts[1], parts[2]); if (server) { builder.usingServer(server.address()); + } else if (remoteUrl) { + builder.usingServer(remoteUrl); } - return driver = realBuild.call(builder); + builder.disableEnvironmentOverrides(); + return realBuild.call(builder); }; return builder; }; - - this.createDriver = function() { - if (!driver) { - driver = this.builder().build(); - } - return driver; - }; - - this.refreshDriver = function() { - if (driver) { - driver.quit(); - driver = null; - } - this.createDriver(); - }; - - this.dispose = function() { - if (driver) { - driver.quit(); - driver = null; - } - }; - - this.waitForTitleToBe = function(expected) { - driver.wait(function() { - return driver.getTitle().then(function(title) { - return title === expected; - }); - }, 5000, 'Waiting for title to be ' + expected); - }; } @@ -193,43 +176,52 @@ function suite(fn, opt_options) { // Filter out browser specific tests when that browser is not currently // selected for testing. browsers = browsers.filter(function(browser) { - if (browsersToTest.indexOf(browser) != -1) { - return true; - } - return browsersToTest.indexOf( - browser.substring('remote.'.length)) != -1; + return browsersToTest.indexOf(browser) != -1; }); } else { browsers = browsersToTest; } try { - browsers.forEach(function(browser) { + // Server is only started if required for a specific config. + testing.after(function() { + if (seleniumServer) { + return seleniumServer.stop(); + } + }); + + browsers.forEach(function(browser) { testing.describe('[' + browser + ']', function() { - var serverToUse = null; - if (NATIVE_BROWSERS.indexOf(browser) == -1) { - serverToUse = seleniumServer; - if (!serverToUse) { - serverToUse = seleniumServer = new seleniumserver.Server(); + if (_base.isDevMode() && nativeRun) { + if (browser === webdriver.Browser.FIREFOX) { + testing.before(function() { + return build.of('//javascript/firefox-driver:webdriver') + .onlyOnce().go(); + }); + } else if (browser === webdriver.Browser.SAFARI) { + testing.before(function() { + return build.of('//javascript/safari-driver:client') + .onlyOnce().go(); + }); } - testing.before(seleniumServer.start.bind(seleniumServer, 60 * 1000)); } - var env = new TestEnvironment(browser, serverToUse); + var serverToUse = null; - testing.beforeEach(function() { - if (env.autoCreateDriver) { - env.createDriver(); + if (!!serverJar && !remoteUrl) { + if (!(serverToUse = seleniumServer)) { + serverToUse = seleniumServer = new remote.SeleniumServer( + serverJar, {loopback: useLoopback}); } - }); - testing.after(function() { - env.dispose(); - }); - - fn(env); + testing.before(function() { + this.timeout(0); + return seleniumServer.start(60 * 1000); + }); + } + fn(new TestEnvironment(browser, serverToUse)); }); }); } finally { @@ -240,18 +232,17 @@ function suite(fn, opt_options) { // GLOBAL TEST SETUP +testing.before(function() { + // Do not pass register fileserver.start directly with testing.before, + // as start takes an optional port, which before assumes is an async + // callback. + return fileserver.start(); +}); -testing.before(fileserver.start); -testing.after(fileserver.stop); - -// Server is only started if required for a specific config. testing.after(function() { - if (seleniumServer) { - seleniumServer.stop(); - } + return fileserver.stop(); }); - // PUBLIC API @@ -263,6 +254,5 @@ exports.beforeEach = testing.beforeEach; exports.it = testing.it; exports.ignore = testing.ignore; -exports.Browser = Browser; exports.Pages = fileserver.Pages; exports.whereIs = fileserver.whereIs; diff --git a/lib/test/resources.js b/lib/test/resources.js index 2f95828..ccd0eeb 100644 --- a/lib/test/resources.js +++ b/lib/test/resources.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; diff --git a/lib/test/seleniumserver.js b/lib/test/seleniumserver.js index 932a02a..7925972 100644 --- a/lib/test/seleniumserver.js +++ b/lib/test/seleniumserver.js @@ -17,6 +17,7 @@ var assert = require('assert'), fs = require('fs'), + path = require('path'), util = require('util'); var promise = require('../..').promise, @@ -25,8 +26,8 @@ var promise = require('../..').promise, build = require('./build'); -var DEV_MODE_JAR_PATH = - 'build/java/server/src/org/openqa/grid/selenium/selenium-standalone.jar'; +var DEV_MODE_JAR_PATH = path.join(__dirname, '../../../../..', + 'build/java/server/src/org/openqa/grid/selenium/selenium-standalone.jar'); var SELENIUM_SERVER_JAR_ENV = 'SELENIUM_SERVER_JAR'; var PROD_MODE_JAR_PATH = process.env[SELENIUM_SERVER_JAR_ENV]; @@ -59,7 +60,8 @@ function getProdModeJarPath() { function Server() { var jarPath = isDevMode ? DEV_MODE_JAR_PATH : getProdModeJarPath(); RemoteServer.call(this, jarPath, { - port: 0 + port: 0, + stdio: 'inherit' }); } util.inherits(Server, RemoteServer); diff --git a/lib/webdriver/abstractbuilder.js b/lib/webdriver/abstractbuilder.js index f535f47..274f81a 100644 --- a/lib/webdriver/abstractbuilder.js +++ b/lib/webdriver/abstractbuilder.js @@ -114,6 +114,19 @@ webdriver.AbstractBuilder.prototype.getCapabilities = function() { }; +/** + * Sets the logging preferences for the created session. Preferences may be + * changed by repeated calls, or by calling {@link #withCapabilities}. + * @param {!(webdriver.logging.Preferences|Object.)} prefs The + * desired logging preferences. + * @return {!webdriver.AbstractBuilder} This Builder instance for chain calling. + */ +webdriver.AbstractBuilder.prototype.setLoggingPreferences = function(prefs) { + this.capabilities_.set(webdriver.Capability.LOGGING_PREFS, prefs); + return this; +}; + + /** * Builds a new {@link webdriver.WebDriver} instance using this builder's * current configuration. diff --git a/lib/webdriver/actionsequence.js b/lib/webdriver/actionsequence.js index cd42979..28106ac 100644 --- a/lib/webdriver/actionsequence.js +++ b/lib/webdriver/actionsequence.js @@ -1,17 +1,19 @@ -// Copyright 2012 Selenium comitters -// Copyright 2012 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. goog.provide('webdriver.ActionSequence'); @@ -27,15 +29,15 @@ goog.require('webdriver.Key'); * Class for defining sequences of complex user interactions. Each sequence * will not be executed until {@link #perform} is called. * - *

          Example:

          
          - *   new webdriver.ActionSequence(driver).
          - *       keyDown(webdriver.Key.SHIFT).
          - *       click(element1).
          - *       click(element2).
          - *       dragAndDrop(element3, element4).
          - *       keyUp(webdriver.Key.SHIFT).
          - *       perform();
          - * 
          + * Example: + * + * new webdriver.ActionSequence(driver). + * keyDown(webdriver.Key.SHIFT). + * click(element1). + * click(element2). + * dragAndDrop(element3, element4). + * keyUp(webdriver.Key.SHIFT). + * perform(); * * @param {!webdriver.WebDriver} driver The driver instance to use. * @constructor @@ -103,13 +105,7 @@ webdriver.ActionSequence.prototype.mouseMove = function(location, opt_offset) { if (goog.isNumber(location.x)) { setOffset(/** @type {{x: number, y: number}} */(location)); } else { - // The interactions API expect the element ID to be encoded as a simple - // string, not the usual JSON object. - var id = /** @type {!webdriver.WebElement} */ (location).toWireValue(). - then(function(value) { - return value['ELEMENT']; - }); - command.setParameter('element', id); + command.setParameter('element', location.getRawId()); if (opt_offset) { setOffset(opt_offset); } @@ -167,12 +163,13 @@ webdriver.ActionSequence.prototype.scheduleMouseAction_ = function( * sequence or another. The behavior for out-of-order events (e.g. mouseDown, * click) is undefined. * - *

          If an element is provided, the mouse will first be moved to the center + * If an element is provided, the mouse will first be moved to the center * of that element. This is equivalent to: - *

          sequence.mouseMove(element).mouseDown()
          * - *

          Warning: this method currently only supports the left mouse button. See - * http://code.google.com/p/selenium/issues/detail?id=4047 + * sequence.mouseMove(element).mouseDown() + * + * Warning: this method currently only supports the left mouse button. See + * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047). * * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either * the element to interact with or the button to click with. @@ -194,12 +191,13 @@ webdriver.ActionSequence.prototype.mouseDown = function(opt_elementOrButton, * Releases a mouse button. Behavior is undefined for calling this function * without a previous call to {@link #mouseDown}. * - *

          If an element is provided, the mouse will first be moved to the center + * If an element is provided, the mouse will first be moved to the center * of that element. This is equivalent to: - *

          sequence.mouseMove(element).mouseUp()
          * - *

          Warning: this method currently only supports the left mouse button. See - * http://code.google.com/p/selenium/issues/detail?id=4047 + * sequence.mouseMove(element).mouseUp() + * + * Warning: this method currently only supports the left mouse button. See + * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047). * * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either * the element to interact with or the button to click with. @@ -234,9 +232,10 @@ webdriver.ActionSequence.prototype.dragAndDrop = function(element, location) { /** * Clicks a mouse button. * - *

          If an element is provided, the mouse will first be moved to the center + * If an element is provided, the mouse will first be moved to the center * of that element. This is equivalent to: - *

          sequence.mouseMove(element).click()
          + * + * sequence.mouseMove(element).click() * * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either * the element to interact with or the button to click with. @@ -257,12 +256,13 @@ webdriver.ActionSequence.prototype.click = function(opt_elementOrButton, /** * Double-clicks a mouse button. * - *

          If an element is provided, the mouse will first be moved to the center of + * If an element is provided, the mouse will first be moved to the center of * that element. This is equivalent to: - *

          sequence.mouseMove(element).doubleClick()
          * - *

          Warning: this method currently only supports the left mouse button. See - * http://code.google.com/p/selenium/issues/detail?id=4047 + * sequence.mouseMove(element).doubleClick() + * + * Warning: this method currently only supports the left mouse button. See + * [issue 4047](http://code.google.com/p/selenium/issues/detail?id=4047). * * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either * the element to interact with or the button to click with. diff --git a/lib/webdriver/builder.js b/lib/webdriver/builder.js index 28d1721..f67368d 100644 --- a/lib/webdriver/builder.js +++ b/lib/webdriver/builder.js @@ -1,70 +1,145 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. goog.provide('webdriver.Builder'); +goog.require('goog.Uri'); goog.require('goog.userAgent'); -goog.require('webdriver.AbstractBuilder'); +goog.require('webdriver.Capabilities'); goog.require('webdriver.FirefoxDomExecutor'); goog.require('webdriver.WebDriver'); goog.require('webdriver.http.CorsClient'); goog.require('webdriver.http.Executor'); goog.require('webdriver.http.XhrClient'); -goog.require('webdriver.process'); /** + * Creates new {@code webdriver.WebDriver} clients for use in a browser + * environment. Upon instantiation, each Builder will configure itself based + * on the following query parameters: + *

          + *
          wdurl + *
          Defines the WebDriver server to send commands to. If this is a + * relative URL, the builder will use the standard WebDriver wire + * protocol and a {@link webdriver.http.XhrClient}. Otherwise, it will + * use a {@link webdriver.http.CorsClient}; this only works when + * connecting to an instance of the Java Selenium server. The server URL + * may be changed using {@code #usingServer}. + * + *
          wdsid + *
          Defines the session to connect to. If omitted, will request a new + * session from the server. + *
          + * + * @param {Window=} opt_window The window to extract query parameters from. * @constructor - * @extends {webdriver.AbstractBuilder} + * @final + * @struct */ -webdriver.Builder = function() { - goog.base(this); - - /** - * ID of an existing WebDriver session that new clients should use. - * Initialized from the value of the - * {@link webdriver.AbstractBuilder.SESSION_ID_ENV} environment variable, but - * may be overridden using - * {@link webdriver.AbstractBuilder#usingSession}. - * @private {string} - */ +webdriver.Builder = function(opt_window) { + var win = opt_window || window; + var data = new goog.Uri(win.location).getQueryData(); + + /** @private {string} */ + this.serverUrl_ = + /** @type {string} */ (data.get(webdriver.Builder.SERVER_URL_PARAM, + webdriver.Builder.DEFAULT_SERVER_URL)).replace(/\/$/, ""); + + /** @private {string} */ this.sessionId_ = - webdriver.process.getEnv(webdriver.Builder.SESSION_ID_ENV); + /** @type {string} */ (data.get(webdriver.Builder.SESSION_ID_PARAM)); + + /** @private {boolean} */ + this.useBrowserCors_ = + /** @type {boolean} */ (data.containsKey(webdriver.Builder.USE_BROWSER_CORS)); + + /** @private {!webdriver.Capabilities} */ + this.capabilities_ = new webdriver.Capabilities(); }; -goog.inherits(webdriver.Builder, webdriver.AbstractBuilder); /** - * 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 {@code #useExistingSession(boolean)}. The use of this environment - * variable requires that {@link webdriver.AbstractBuilder.SERVER_URL_ENV} also - * be set. + * Query parameter that defines which session to connect to. + * @type {string} + * @const + */ +webdriver.Builder.SESSION_ID_PARAM = 'wdsid'; + + +/** + * Query parameter that defines the URL of the remote server to connect to. + * @type {string} + * @const + */ +webdriver.Builder.SERVER_URL_PARAM = 'wdurl'; + + +/** + * The default server URL to use. + * @type {string} + * @const + */ +webdriver.Builder.DEFAULT_SERVER_URL = 'http://localhost:4444/wd/hub'; + + +/** + * Query parameter that defines whether browser CORS support should be used, + * if available. * @type {string} * @const - * @see webdriver.process.getEnv */ -webdriver.Builder.SESSION_ID_ENV = 'wdsid'; +webdriver.Builder.USE_BROWSER_CORS = 'wdcors'; + + +/** + * Configures the WebDriver to use browser's CORS, if available. + * @return {!webdriver.Builder} This Builder instance for chain calling. + */ +webdriver.Builder.prototype.useBrowserCors = function() { + this.useBrowserCors_ = true; + return this; +}; + +/** + * Configures which WebDriver server should be used for new sessions. + * @param {string} url URL of the server to use. + * @return {!webdriver.Builder} This Builder instance for chain calling. + */ +webdriver.Builder.prototype.usingServer = function(url) { + this.serverUrl_ = url.replace(/\/$/, ""); + return this; +}; + + +/** + * @return {string} The URL of the WebDriver server this instance is configured + * to use. + */ +webdriver.Builder.prototype.getServerUrl = function() { + return this.serverUrl_; +}; /** * Configures the builder to create a client that will use an existing WebDriver * session. * @param {string} id The existing session ID to use. - * @return {!webdriver.AbstractBuilder} This Builder instance for chain calling. + * @return {!webdriver.Builder} This Builder instance for chain calling. */ webdriver.Builder.prototype.usingSession = function(id) { this.sessionId_ = id; @@ -82,7 +157,22 @@ webdriver.Builder.prototype.getSession = function() { /** - * @override + * Sets the desired capabilities when requesting a new session. This will + * overwrite any previously set desired capabilities. + * @param {!(Object|webdriver.Capabilities)} capabilities The desired + * capabilities for a new session. + * @return {!webdriver.Builder} This Builder instance for chain calling. + */ +webdriver.Builder.prototype.withCapabilities = function(capabilities) { + this.capabilities_ = new webdriver.Capabilities(capabilities); + return this; +}; + + +/** + * Builds a new {@link webdriver.WebDriver} instance using this builder's + * current configuration. + * @return {!webdriver.WebDriver} A new WebDriver client. */ webdriver.Builder.prototype.build = function() { if (goog.userAgent.GECKO && document.readyState != 'complete') { @@ -93,12 +183,13 @@ webdriver.Builder.prototype.build = function() { if (webdriver.FirefoxDomExecutor.isAvailable()) { executor = new webdriver.FirefoxDomExecutor(); - return webdriver.WebDriver.createSession(executor, this.getCapabilities()); + return webdriver.WebDriver.createSession(executor, this.capabilities_); } else { - var url = this.getServerUrl() || - webdriver.AbstractBuilder.DEFAULT_SERVER_URL; + var url = this.serverUrl_; var client; - if (url[0] == '/') { + if (this.useBrowserCors_ && webdriver.http.CorsClient.isAvailable()) { + client = new webdriver.http.XhrClient(url); + } else if (url[0] == '/') { var origin = window.location.origin || (window.location.protocol + '//' + window.location.host); client = new webdriver.http.XhrClient(origin + url); @@ -107,11 +198,10 @@ webdriver.Builder.prototype.build = function() { } executor = new webdriver.http.Executor(client); - if (this.getSession()) { - return webdriver.WebDriver.attachToSession(executor, this.getSession()); + if (this.sessionId_) { + return webdriver.WebDriver.attachToSession(executor, this.sessionId_); } else { - throw new Error('Unable to create a new client for this browser. The ' + - 'WebDriver session ID has not been defined.'); + return webdriver.WebDriver.createSession(executor, this.capabilities_); } } }; diff --git a/lib/webdriver/button.js b/lib/webdriver/button.js index aedb0b3..d45a8dd 100644 --- a/lib/webdriver/button.js +++ b/lib/webdriver/button.js @@ -1,17 +1,19 @@ -// Copyright 2012 Selenium comitters -// Copyright 2012 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. goog.provide('webdriver.Button'); diff --git a/lib/webdriver/capabilities.js b/lib/webdriver/capabilities.js index 783322a..874ca65 100644 --- a/lib/webdriver/capabilities.js +++ b/lib/webdriver/capabilities.js @@ -1,16 +1,19 @@ -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Defines the webdriver.Capabilities class. @@ -19,6 +22,10 @@ goog.provide('webdriver.Browser'); goog.provide('webdriver.Capabilities'); goog.provide('webdriver.Capability'); +goog.provide('webdriver.ProxyConfig'); + +goog.require('webdriver.Serializable'); +goog.require('webdriver.logging'); @@ -30,6 +37,7 @@ webdriver.Browser = { ANDROID: 'android', CHROME: 'chrome', FIREFOX: 'firefox', + IE: 'internet explorer', INTERNET_EXPLORER: 'internet explorer', IPAD: 'iPad', IPHONE: 'iPhone', @@ -41,6 +49,23 @@ webdriver.Browser = { +/** + * Describes how a proxy should be configured for a WebDriver session. + * Proxy configuration object, as defined by the WebDriver wire protocol. + * @typedef {( + * {proxyType: string}| + * {proxyType: string, + * proxyAutoconfigUrl: string}| + * {proxyType: string, + * ftpProxy: string, + * httpProxy: string, + * sslProxy: string, + * noProxy: string})} + */ +webdriver.ProxyConfig; + + + /** * Common webdriver capability keys. * @enum {string} @@ -50,8 +75,7 @@ webdriver.Capability = { /** * Indicates whether a driver should accept all SSL certs by default. This * capability only applies when requesting a new session. To query whether - * a driver can handle insecure SSL certs, see - * {@link webdriver.Capability.SECURE_SSL}. + * a driver can handle insecure SSL certs, see {@link #SECURE_SSL}. */ ACCEPT_SSL_CERTS: 'acceptSslCerts', @@ -62,10 +86,24 @@ webdriver.Capability = { */ BROWSER_NAME: 'browserName', + /** + * New Capability, which describes the browser name for supporting + * testing on BrowserStack + */ + BROWSER: 'browser', + + /** + * Defines how elements should be scrolled into the viewport for interaction. + * This capability will be set to zero (0) if elements are aligned with the + * top of the viewport, or one (1) if aligned with the bottom. The default + * behavior is to align with the top of the viewport. + */ + ELEMENT_SCROLL_BEHAVIOR: 'elementScrollBehavior', + /** * Whether the driver is capable of handling modal alerts (e.g. alert, * confirm, prompt). To define how a driver should handle alerts, - * use {@link webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR}. + * use {@link #UNEXPECTED_ALERT_BEHAVIOR}. */ HANDLES_ALERTS: 'handlesAlerts', @@ -74,6 +112,11 @@ webdriver.Capability = { */ LOGGING_PREFS: 'loggingPrefs', + /** + * Whether this session generates native events when simulating user input. + */ + NATIVE_EVENTS: 'nativeEvents', + /** * Describes the platform the browser is running on. Will be one of * ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When requesting a @@ -93,19 +136,13 @@ webdriver.Capability = { /** * Whether a driver is only capable of handling secure SSL certs. To request * that a driver accept insecure SSL certs by default, use - * {@link webdriver.Capability.ACCEPT_SSL_CERTS}. + * {@link #ACCEPT_SSL_CERTS}. */ SECURE_SSL: 'secureSsl', /** Whether the driver supports manipulating the app cache. */ SUPPORTS_APPLICATION_CACHE: 'applicationCacheEnabled', - /** - * Whether the driver supports controlling the browser's internet - * connectivity. - */ - SUPPORTS_BROWSER_CONNECTION: 'browserConnectionEnabled', - /** Whether the driver supports locating elements with CSS selectors. */ SUPPORTS_CSS_SELECTORS: 'cssSelectorsEnabled', @@ -134,16 +171,19 @@ webdriver.Capability = { * @param {(webdriver.Capabilities|Object)=} opt_other Another set of * capabilities to merge into this instance. * @constructor + * @extends {webdriver.Serializable.>} */ webdriver.Capabilities = function(opt_other) { + webdriver.Serializable.call(this); - /** @private {!Object} */ + /** @private {!Object.} */ this.caps_ = {}; if (opt_other) { this.merge(opt_other); } }; +goog.inherits(webdriver.Capabilities, webdriver.Serializable); /** @@ -214,7 +254,6 @@ webdriver.Capabilities.opera = function() { set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.OPERA); }; - /** * @return {!webdriver.Capabilities} A basic set of capabilities for * PhantomJS. @@ -254,8 +293,12 @@ webdriver.Capabilities.htmlunitwithjs = function() { }; -/** @return {!Object} The JSON representation of this instance. */ -webdriver.Capabilities.prototype.toJSON = function() { +/** + * @return {!Object.} The JSON representation of this instance. Note, + * the returned object may contain nested promises that are promised values. + * @override + */ +webdriver.Capabilities.prototype.serialize = function() { return this.caps_; }; @@ -316,3 +359,59 @@ webdriver.Capabilities.prototype.get = function(key) { webdriver.Capabilities.prototype.has = function(key) { return !!this.get(key); }; + + +/** + * Sets the logging preferences. Preferences may be specified as a + * {@link webdriver.logging.Preferences} instance, or a as a map of log-type to + * log-level. + * @param {!(webdriver.logging.Preferences|Object.)} prefs The + * logging preferences. + * @return {!webdriver.Capabilities} A self reference. + */ +webdriver.Capabilities.prototype.setLoggingPrefs = function(prefs) { + return this.set(webdriver.Capability.LOGGING_PREFS, prefs); +}; + + +/** + * Sets the proxy configuration for this instance. + * @param {webdriver.ProxyConfig} proxy The desired proxy configuration. + * @return {!webdriver.Capabilities} A self reference. + */ +webdriver.Capabilities.prototype.setProxy = function(proxy) { + return this.set(webdriver.Capability.PROXY, proxy); +}; + + +/** + * Sets whether native events should be used. + * @param {boolean} enabled Whether to enable native events. + * @return {!webdriver.Capabilities} A self reference. + */ +webdriver.Capabilities.prototype.setEnableNativeEvents = function(enabled) { + return this.set(webdriver.Capability.NATIVE_EVENTS, enabled); +}; + + +/** + * Sets how elements should be scrolled into view for interaction. + * @param {number} behavior The desired scroll behavior: either 0 to align with + * the top of the viewport or 1 to align with the bottom. + * @return {!webdriver.Capabilities} A self reference. + */ +webdriver.Capabilities.prototype.setScrollBehavior = function(behavior) { + return this.set(webdriver.Capability.ELEMENT_SCROLL_BEHAVIOR, behavior); +}; + + +/** + * Sets the default action to take with an unexpected alert before returning + * an error. + * @param {string} behavior The desired behavior; should be "accept", "dismiss", + * or "ignore". Defaults to "dismiss". + * @return {!webdriver.Capabilities} A self reference. + */ +webdriver.Capabilities.prototype.setAlertBehavior = function(behavior) { + return this.set(webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR, behavior); +}; diff --git a/lib/webdriver/command.js b/lib/webdriver/command.js index 5c3d95a..f501179 100644 --- a/lib/webdriver/command.js +++ b/lib/webdriver/command.js @@ -1,16 +1,19 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Contains several classes for handling commands. @@ -199,9 +202,9 @@ webdriver.CommandName = { // optional for these commands. CLICK: 'mouseClick', DOUBLE_CLICK: 'mouseDoubleClick', - MOUSE_DOWN: 'mouseDown', - MOUSE_UP: 'mouseUp', - MOVE_TO: 'mouseMove', + MOUSE_DOWN: 'mouseButtonDown', + MOUSE_UP: 'mouseButtonUp', + MOVE_TO: 'mouseMoveTo', SEND_KEYS_TO_ACTIVE_ELEMENT: 'sendKeysToActiveElement', // These belong to the Advanced Touch API @@ -216,13 +219,16 @@ webdriver.CommandName = { GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes', GET_LOG: 'getLog', - GET_SESSION_LOGS: 'getSessionLogs' + GET_SESSION_LOGS: 'getSessionLogs', + + // Non-standard commands used by the standalone Selenium server. + UPLOAD_FILE: 'uploadFile' }; /** - * Handles the execution of {@code webdriver.Command} objects. + * Handles the execution of WebDriver {@link webdriver.Command commands}. * @interface */ webdriver.CommandExecutor = function() {}; diff --git a/lib/webdriver/events.js b/lib/webdriver/events.js index a420163..cad3bac 100644 --- a/lib/webdriver/events.js +++ b/lib/webdriver/events.js @@ -1,16 +1,19 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. /** * @fileoverview A light weight event system modeled after Node's EventEmitter. diff --git a/lib/webdriver/firefoxdomexecutor.js b/lib/webdriver/firefoxdomexecutor.js index b581f67..397cbaf 100644 --- a/lib/webdriver/firefoxdomexecutor.js +++ b/lib/webdriver/firefoxdomexecutor.js @@ -1,23 +1,26 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. goog.provide('webdriver.FirefoxDomExecutor'); goog.require('bot.response'); -goog.require('goog.json'); goog.require('goog.userAgent.product'); goog.require('webdriver.Command'); +goog.require('webdriver.CommandExecutor'); goog.require('webdriver.CommandName'); @@ -113,12 +116,9 @@ webdriver.FirefoxDomExecutor.prototype.execute = function(command, callback) { command.getName() != webdriver.CommandName.SWITCH_TO_FRAME) { parameters['id'] = parameters['id']['ELEMENT']; } - - var json = goog.json.serialize({ + var json = JSON.stringify({ 'name': command.getName(), - 'sessionId': { - 'value': parameters['sessionId'] - }, + 'sessionId': parameters['sessionId'], 'parameters': parameters }); this.docElement_.setAttribute( @@ -155,7 +155,7 @@ webdriver.FirefoxDomExecutor.prototype.onResponse_ = function() { try { var response = bot.response.checkResponse( - /** @type {!bot.response.ResponseObject} */ (goog.json.parse(json))); + /** @type {!bot.response.ResponseObject} */ (JSON.parse(json))); } catch (ex) { command.callback(ex); return; diff --git a/lib/webdriver/http/corsclient.js b/lib/webdriver/http/corsclient.js index b0ea15a..262106b 100644 --- a/lib/webdriver/http/corsclient.js +++ b/lib/webdriver/http/corsclient.js @@ -1,20 +1,23 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. goog.provide('webdriver.http.CorsClient'); -goog.require('goog.json'); +goog.require('webdriver.http.Client'); goog.require('webdriver.http.Response'); @@ -47,10 +50,10 @@ goog.require('webdriver.http.Response'); * This limitation appears to be intentional and is documented in WebKit's * Layout tests: * //LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html - *
        • If the server does not return a 2xx response, IE and Opera's - * implementations will fire the XDomainRequest/XMLHttpRequest object's + *
        • If the server does not return a 2xx response, IE + * implementation will fire the XDomainRequest/XMLHttpRequest object's * onerror handler, but without the corresponding response text returned by - * the server. This renders IE and Opera incapable of handling command + * the server. This renders IE incapable of handling command * failures in the standard JSON protocol. *
        * @@ -58,7 +61,7 @@ goog.require('webdriver.http.Response'); * @constructor * @implements {webdriver.http.Client} * @see CORS Spec - * @see + * @see * JSON wire protocol */ webdriver.http.CorsClient = function(url) { @@ -119,7 +122,7 @@ webdriver.http.CorsClient.prototype.send = function(request, callback) { // optimized away by the compiler, which leaves us where we were before. xhr.onprogress = xhr.ontimeout = function() {}; - xhr.send(goog.json.serialize({ + xhr.send(JSON.stringify({ 'method': request.method, 'path': request.path, 'data': request.data diff --git a/lib/webdriver/http/http.js b/lib/webdriver/http/http.js index 3adff37..ab0d1bd 100644 --- a/lib/webdriver/http/http.js +++ b/lib/webdriver/http/http.js @@ -1,16 +1,19 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Defines a {@code webdriver.CommandExecutor} that communicates @@ -24,9 +27,10 @@ goog.provide('webdriver.http.Response'); goog.require('bot.ErrorCode'); goog.require('goog.array'); -goog.require('goog.json'); +goog.require('webdriver.CommandExecutor'); goog.require('webdriver.CommandName'); -goog.require('webdriver.promise.Deferred'); +goog.require('webdriver.logging'); +goog.require('webdriver.promise'); @@ -41,9 +45,9 @@ webdriver.http.Client = function() { /** * 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 {@code Error} describing the error. Otherwise, when + * invoked with a non-null {@link 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 {@code webdriver.http.Response} object. + * null Error and non-null {@link webdriver.http.Response} object. * * @param {!webdriver.http.Request} request The request to send. * @param {function(Error, !webdriver.http.Response=)} callback the function to @@ -69,12 +73,43 @@ webdriver.http.Executor = function(client) { * @private {!webdriver.http.Client} */ this.client_ = client; + + /** + * @private {!Object<{method:string, path:string}>} + */ + this.customCommands_ = {}; + + /** + * @private {!webdriver.logging.Logger} + */ + this.log_ = webdriver.logging.getLogger('webdriver.http.Executor'); +}; + + +/** + * Defines a new command for use with this executor. When a command is sent, + * the {@code path} will be preprocessed using the command's parameters; any + * path segments prefixed with ":" will be replaced by the parameter of the + * same name. For example, given "/person/:name" and the parameters + * "{name: 'Bob'}", the final command path will be "/person/Bob". + * + * @param {string} name The command name. + * @param {string} method The HTTP method to use when sending this command. + * @param {string} path The path to send the command to, relative to + * the WebDriver server's command root and of the form + * "/path/:variable/segment". + */ +webdriver.http.Executor.prototype.defineCommand = function( + name, method, path) { + this.customCommands_[name] = {method: method, path: path}; }; /** @override */ webdriver.http.Executor.prototype.execute = function(command, callback) { - var resource = webdriver.http.Executor.COMMAND_MAP_[command.getName()]; + var resource = + this.customCommands_[command.getName()] || + webdriver.http.Executor.COMMAND_MAP_[command.getName()]; if (!resource) { throw new Error('Unrecognized command: ' + command.getName()); } @@ -84,13 +119,22 @@ webdriver.http.Executor.prototype.execute = function(command, callback) { var request = new webdriver.http.Request(resource.method, path, parameters); request.headers['connection'] = command.getName() == 'quit'? 'close' : 'keep-alive'; + var log = this.log_; + log.finer(function() { + return '>>>\n' + request; + }); + this.client_.send(request, function(e, response) { var responseObj; if (!e) { + log.finer(function() { + return '<<<\n' + response; + }); try { responseObj = webdriver.http.Executor.parseHttpResponse_( /** @type {!webdriver.http.Response} */ (response)); } catch (ex) { + log.warning('Error parsing response', ex); e = ex; } } @@ -145,7 +189,7 @@ webdriver.http.Executor.buildPath_ = function(path, parameters) { */ webdriver.http.Executor.parseHttpResponse_ = function(httpResponse) { try { - return /** @type {!bot.response.ResponseObject} */ (goog.json.parse( + return /** @type {!bot.response.ResponseObject} */ (JSON.parse( httpResponse.body)); } catch (ex) { // Whoops, looks like the server sent us a malformed response. We'll need @@ -280,6 +324,22 @@ webdriver.http.Executor.COMMAND_MAP_ = (function() { put(webdriver.CommandName.MOVE_TO, post('/session/:sessionId/moveto')). put(webdriver.CommandName.SEND_KEYS_TO_ACTIVE_ELEMENT, post('/session/:sessionId/keys')). + put(webdriver.CommandName.TOUCH_SINGLE_TAP, + post('/session/:sessionId/touch/click')). + put(webdriver.CommandName.TOUCH_DOUBLE_TAP, + post('/session/:sessionId/touch/doubleclick')). + put(webdriver.CommandName.TOUCH_DOWN, + post('/session/:sessionId/touch/down')). + put(webdriver.CommandName.TOUCH_UP, + post('/session/:sessionId/touch/up')). + put(webdriver.CommandName.TOUCH_MOVE, + post('/session/:sessionId/touch/move')). + put(webdriver.CommandName.TOUCH_SCROLL, + post('/session/:sessionId/touch/scroll')). + put(webdriver.CommandName.TOUCH_LONG_PRESS, + post('/session/:sessionId/touch/longclick')). + put(webdriver.CommandName.TOUCH_FLICK, + post('/session/:sessionId/touch/flick')). put(webdriver.CommandName.ACCEPT_ALERT, post('/session/:sessionId/accept_alert')). put(webdriver.CommandName.DISMISS_ALERT, @@ -292,6 +352,7 @@ webdriver.http.Executor.COMMAND_MAP_ = (function() { put(webdriver.CommandName.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/log/types')). put(webdriver.CommandName.GET_SESSION_LOGS, post('/logs')). + put(webdriver.CommandName.UPLOAD_FILE, post('/session/:sessionId/file')). build(); /** @constructor */ @@ -334,7 +395,7 @@ webdriver.http.headersToString_ = function(headers) { /** * 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 - * {@code webdriver.http.Client}'s responsibility to build the full URL for the + * {@link webdriver.http.Client}'s responsibility to build the full URL for the * final request. * @param {string} method The HTTP method to use for the request. * @param {string} path Path on the server to send the request to. @@ -375,7 +436,7 @@ webdriver.http.Request.prototype.toString = function() { this.method + ' ' + this.path + ' HTTP/1.1', webdriver.http.headersToString_(this.headers), '', - goog.json.serialize(this.data) + JSON.stringify(this.data) ].join('\n'); }; @@ -415,8 +476,8 @@ webdriver.http.Response = function(status, headers, body) { /** - * Builds a {@code webdriver.http.Response} from a {@code XMLHttpRequest} or - * {@code XDomainRequest} response object. + * Builds a {@link webdriver.http.Response} from a {@link XMLHttpRequest} or + * {@link XDomainRequest} response object. * @param {!(XDomainRequest|XMLHttpRequest)} xhr The request to parse. * @return {!webdriver.http.Response} The parsed response. */ diff --git a/lib/webdriver/http/xhrclient.js b/lib/webdriver/http/xhrclient.js index 4f69fc3..58c69a3 100644 --- a/lib/webdriver/http/xhrclient.js +++ b/lib/webdriver/http/xhrclient.js @@ -1,23 +1,26 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. /** @fileoverview A XHR client. */ goog.provide('webdriver.http.XhrClient'); -goog.require('goog.json'); goog.require('goog.net.XmlHttp'); +goog.require('webdriver.http.Client'); goog.require('webdriver.http.Response'); @@ -57,7 +60,7 @@ webdriver.http.XhrClient.prototype.send = function(request, callback) { xhr.setRequestHeader(header, request.headers[header] + ''); } - xhr.send(goog.json.serialize(request.data)); + xhr.send(JSON.stringify(request.data)); } catch (ex) { callback(ex); } diff --git a/lib/webdriver/key.js b/lib/webdriver/key.js index e231581..af8ba61 100644 --- a/lib/webdriver/key.js +++ b/lib/webdriver/key.js @@ -1,16 +1,19 @@ -// Copyright 2012 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. goog.provide('webdriver.Key'); diff --git a/lib/webdriver/locators.js b/lib/webdriver/locators.js index 8d993d8..fde52e9 100644 --- a/lib/webdriver/locators.js +++ b/lib/webdriver/locators.js @@ -1,16 +1,19 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Factory methods for the supported locator strategies. @@ -73,14 +76,13 @@ goog.exportSymbol('By', webdriver.By); /** * Short-hand expressions for the primary element locator strategies. * For example the following two statements are equivalent: - *
        - * var e1 = driver.findElement(webdriver.By.id('foo'));
        - * var e2 = driver.findElement({id: 'foo'});
        - * 
        * - *

        Care should be taken when using JavaScript minifiers (such as the + * var e1 = driver.findElement(webdriver.By.id('foo')); + * var e2 = driver.findElement({id: 'foo'}); + * + * 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. + * the un-obfuscated properties listed. * * @typedef {( * {className: string}| @@ -111,7 +113,7 @@ webdriver.By.className = webdriver.Locator.factory_('class name'); /** * Locates elements using a CSS selector. For browsers that do not support * CSS selectors, WebDriver implementations may return an - * {@link bot.Error.State.INVALID_SELECTOR invalid selector} error. An + * {@linkplain bot.Error.State.INVALID_SELECTOR invalid selector} error. An * implementation may, however, emulate the CSS selector API. * * @param {string} selector The CSS selector to use. @@ -131,7 +133,7 @@ webdriver.By.id = webdriver.Locator.factory_('id'); /** - * Locates link elements whose {@link webdriver.WebElement#getText visible + * Locates link elements whose {@linkplain webdriver.WebElement#getText visible * text} matches the given string. * * @param {string} text The link text to search for. @@ -142,7 +144,7 @@ webdriver.By.linkText = webdriver.Locator.factory_('link text'); /** * Locates an elements by evaluating a - * {@link webdriver.WebDriver#executeScript JavaScript expression}. + * {@linkplain webdriver.WebDriver#executeScript JavaScript expression}. * The result of this expression must be an element or list of elements. * * @param {!(string|Function)} script The script to execute. @@ -168,7 +170,7 @@ webdriver.By.name = webdriver.Locator.factory_('name'); /** - * Locates link elements whose {@link webdriver.WebElement#getText visible + * Locates link elements whose {@linkplain webdriver.WebElement#getText visible * text} contains the given substring. * * @param {string} text The substring to check for in a link's visible text. @@ -180,7 +182,9 @@ webdriver.By.partialLinkText = webdriver.Locator.factory_( /** * Locates elements with a given tag name. The returned locator is - * equivalent to using the {@code getElementsByTagName} DOM function. + * equivalent to using the + * [getElementsByTagName](https://developer.mozilla.org/en-US/docs/Web/API/Element.getElementsByTagName) + * DOM function. * * @param {string} text The substring to check for in a link's visible text. * @return {!webdriver.Locator} The new locator. diff --git a/lib/webdriver/logging.js b/lib/webdriver/logging.js index 775f758..985cee8 100644 --- a/lib/webdriver/logging.js +++ b/lib/webdriver/logging.js @@ -1,49 +1,176 @@ -// Copyright 2013 Selenium comitters -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Defines WebDriver's logging system. The logging system is + * broken into major components: local and remote logging. + * + * The local logging API, which is anchored by the + * {@link webdriver.logging.Logger Logger} class, is similar to Java's + * logging API. Loggers, retrieved by {@link webdriver.logging.getLogger}, use + * hierarchical, dot-delimited namespaces + * (e.g. "" > "webdriver" > "webdriver.logging"). Recorded log messages are + * represented by the {@link webdriver.logging.LogRecord LogRecord} class. You + * can capture log records by + * {@linkplain webdriver.logging.Logger#addHandler attaching} a handler function + * to the desired logger. For convenience, you can quickly enable logging to + * the console by simply calling + * {@link webdriver.logging.installConsoleHandler()}. + * + * The [remote logging API](https://github.com/SeleniumHQ/selenium/wiki/Logging) + * allows you to retrieve logs from a remote WebDriver server. This API uses the + * {@link Preferences} class to define desired log levels prior to create a + * WebDriver session: + * + * var prefs = new webdriver.logging.Preferences(); + * prefs.setLevel(webdriver.logging.Type.BROWSER, + * webdriver.logging.Level.DEBUG); + * + * var caps = webdriver.Capabilities.chrome(); + * caps.setLoggingPrefs(prefs); + * // ... + * + * Remote log entries are represented by the {@link Entry} class and may be + * retrieved via {@link webdriver.WebDriver.Logs}: + * + * driver.manage().logs().get(webdriver.logging.Type.BROWSER) + * .then(function(entries) { + * entries.forEach(function(entry) { + * console.log('[%s] %s', entry.level.name, entry.message); + * }); + * }); + * + * **NOTE:** Only a few browsers support the remote logging API (notably + * Firefox and Chrome). Firefox supports basic logging functionality, while + * Chrome exposes robust + * [performance logging](https://sites.google.com/a/chromium.org/chromedriver/logging) + * options. Remote logging is still considered a non-standard feature, and the + * APIs exposed by this module for it are non-frozen. Once logging is officially + * defined by the [W3C WebDriver spec](http://www.w3.org/TR/webdriver/), this + * module will be updated to use a consistent API for local and remote logging. + */ + +goog.module('webdriver.logging'); +goog.module.declareLegacyNamespace(); + +var LogManager = goog.require('goog.debug.LogManager'); +var LogRecord = goog.require('goog.debug.LogRecord'); +var Logger = goog.require('goog.debug.Logger'); +var Objects = goog.require('goog.object'); +var padNumber = goog.require('goog.string').padNumber; + -goog.provide('webdriver.logging'); -goog.provide('webdriver.logging.Preferences'); +/** @const */ +exports.LogRecord = LogRecord; -goog.require('goog.object'); + +/** @const */ +exports.Logger = Logger; + + +/** @const */ +exports.Level = Logger.Level; /** - * Log level names from WebDriver's JSON wire protocol. - * @enum {string} + * DEBUG is a message level for debugging messages and has the same log level + * as the {@link Logger.Level.CONFIG} message level. + * @const {!Logger.Level} + */ +Logger.Level.DEBUG = new Logger.Level('DEBUG', Logger.Level.CONFIG.value); + + +/** + * Finds a named logger. + * + * @param {string=} opt_name The dot-delimited logger name, such as + * "webdriver.logging.Logger". Defaults to the name of the root logger. + * @return {!Logger} The named logger. + */ +function getLogger(opt_name) { + return LogManager.getLogger(opt_name || Logger.ROOT_LOGGER_NAME); +} +exports.getLogger = getLogger; + + +/** + * Logs all messages to the Console API. + */ +function consoleHandler(record) { + if (typeof console === 'undefined' || !console) { + return; + } + record = /** @type {!LogRecord} */(record); + var timestamp = new Date(record.getMillis()); + var msg = + '[' + timestamp.getUTCFullYear() + '-' + + padNumber(timestamp.getUTCMonth() + 1, 2) + '-' + + padNumber(timestamp.getUTCDate(), 2) + 'T' + + padNumber(timestamp.getUTCHours(), 2) + ':' + + padNumber(timestamp.getUTCMinutes(), 2) + ':' + + padNumber(timestamp.getUTCSeconds(), 2) + 'Z]' + + '[' + record.getLevel().name + ']' + + '[' + record.getLoggerName() + '] ' + + record.getMessage(); + + var level = record.getLevel().value; + if (level >= Logger.Level.SEVERE.value) { + console.error(msg); + } else if (level >= Logger.Level.WARNING.value) { + console.warn(msg); + } else { + console.log(msg); + } +} + + +/** + * Adds the console handler to the given logger. The console handler will log + * all messages using the JavaScript Console API. + * + * @param {Logger=} opt_logger The logger to add the handler to; defaults + * to the root logger. + * @see exports.removeConsoleHandler */ -webdriver.logging.LevelName = { - ALL: 'ALL', - DEBUG: 'DEBUG', - INFO: 'INFO', - WARNING: 'WARNING', - SEVERE: 'SEVERE', - OFF: 'OFF' +exports.addConsoleHandler = function(opt_logger) { + var logger = opt_logger || getLogger(); + logger.addHandler(consoleHandler); }; /** - * Logging levels. - * @enum {{value: number, name: webdriver.logging.LevelName}} + * Installs the console log handler on the root logger. + * @see exports.addConsoleHandler */ -webdriver.logging.Level = { - ALL: {value: Number.MIN_VALUE, name: webdriver.logging.LevelName.ALL}, - DEBUG: {value: 700, name: webdriver.logging.LevelName.DEBUG}, - INFO: {value: 800, name: webdriver.logging.LevelName.INFO}, - WARNING: {value: 900, name: webdriver.logging.LevelName.WARNING}, - SEVERE: {value: 1000, name: webdriver.logging.LevelName.SEVERE}, - OFF: {value: Number.MAX_VALUE, name: webdriver.logging.LevelName.OFF} +exports.installConsoleHandler = function() { + exports.addConsoleHandler(); +}; + + +/** + * Removes the console log handler from the given logger. + * + * @param {Logger=} opt_logger The logger to remove the handler from; defaults + * to the root logger. + * @see exports.addConsoleHandler + */ +exports.removeConsoleHandler = function(opt_logger) { + var logger = opt_logger || getLogger(); + logger.removeHandler(consoleHandler); }; @@ -53,23 +180,58 @@ webdriver.logging.Level = { * will be returned. * @param {(number|string)} nameOrValue The log level name, or value, to * convert . - * @return {!webdriver.logging.Level} The converted level. + * @return {!Logger.Level} The converted level. */ -webdriver.logging.getLevel = function(nameOrValue) { - var predicate = goog.isString(nameOrValue) ? - function(val) { return val.name === nameOrValue; } : - function(val) { return val.value === nameOrValue; }; +function getLevel(nameOrValue) { + // DEBUG is not a predefined Closure log level, but maps to CONFIG. Since + // DEBUG is a predefined level for the WebDriver protocol, we prefer it over + // CONFIG. + if ('DEBUG' === nameOrValue || Logger.Level.DEBUG.value === nameOrValue) { + return Logger.Level.DEBUG; + } else if (goog.isString(nameOrValue)) { + return Logger.Level.getPredefinedLevel(/** @type {string} */(nameOrValue)) + || Logger.Level.ALL; + } else { + return Logger.Level.getPredefinedLevelByValue( + /** @type {number} */(nameOrValue)) || Logger.Level.ALL; + } +} +exports.getLevel = getLevel; - return goog.object.findValue(webdriver.logging.Level, predicate) || - webdriver.logging.Level.ALL; -}; + +/** + * Normalizes a {@link Logger.Level} to one of the distinct values recognized + * by WebDriver's wire protocol. + * @param {!Logger.Level} level The input level. + * @return {!Logger.Level} The normalized level. + */ +function normalizeLevel(level) { + if (level.value <= Logger.Level.ALL.value) { // ALL is 0. + return Logger.Level.ALL; + + } else if (level.value === Logger.Level.OFF.value) { // OFF is Infinity + return Logger.Level.OFF; + + } else if (level.value < Logger.Level.INFO.value) { + return Logger.Level.DEBUG; + + } else if (level.value < Logger.Level.WARNING.value) { + return Logger.Level.INFO; + + } else if (level.value < Logger.Level.SEVERE.value) { + return Logger.Level.WARNING; + + } else { + return Logger.Level.SEVERE; + } +} /** * Common log types. * @enum {string} */ -webdriver.logging.Type = { +var Type = { /** Logs originating from the browser. */ BROWSER: 'browser', /** Logs from a WebDriver client. */ @@ -81,78 +243,105 @@ webdriver.logging.Type = { /** Logs from the remote server. */ SERVER: 'server' }; +exports.Type = Type; /** - * A hash describing log preferences. - * @typedef {Object.} + * Describes the log preferences for a WebDriver session. + * @final */ -webdriver.logging.Preferences; +var Preferences = goog.defineClass(null, { + /** @constructor */ + constructor: function() { + /** @private {!Object.} */ + this.prefs_ = {}; + }, + + /** + * Sets the desired logging level for a particular log type. + * @param {(string|Type)} type The log type. + * @param {!Logger.Level} level The desired log level. + */ + setLevel: function(type, level) { + this.prefs_[type] = normalizeLevel(level); + }, + + /** + * Converts this instance to its JSON representation. + * @return {!Object.} The JSON representation of this set of + * preferences. + */ + toJSON: function() { + var obj = {}; + for (var type in this.prefs_) { + if (this.prefs_.hasOwnProperty(type)) { + obj[type] = this.prefs_[type].name; + } + } + return obj; + } +}); +exports.Preferences = Preferences; /** - * A single log entry. - * @param {(!webdriver.logging.Level|string)} level The entry level. - * @param {string} message The log message. - * @param {number=} opt_timestamp The time this entry was generated, in - * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the - * current time will be used. - * @param {string=} opt_type The log type, if known. - * @constructor + * A single log entry recorded by a WebDriver component, such as a remote + * WebDriver server. + * @final */ -webdriver.logging.Entry = function(level, message, opt_timestamp, opt_type) { +var Entry = goog.defineClass(null, { + /** + * @param {(!Logger.Level|string)} level The entry level. + * @param {string} message The log message. + * @param {number=} opt_timestamp The time this entry was generated, in + * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the + * current time will be used. + * @param {string=} opt_type The log type, if known. + * @constructor + */ + constructor: function(level, message, opt_timestamp, opt_type) { - /** @type {!webdriver.logging.Level} */ - this.level = - goog.isString(level) ? webdriver.logging.getLevel(level) : level; + /** @type {!Logger.Level} */ + this.level = goog.isString(level) ? getLevel(level) : level; - /** @type {string} */ - this.message = message; + /** @type {string} */ + this.message = message; - /** @type {number} */ - this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now(); + /** @type {number} */ + this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now(); - /** @type {string} */ - this.type = opt_type || ''; -}; + /** @type {string} */ + this.type = opt_type || ''; + }, + statics: { + /** + * Converts a {@link goog.debug.LogRecord} into a + * {@link webdriver.logging.Entry}. + * @param {!goog.debug.LogRecord} logRecord The record to convert. + * @param {string=} opt_type The log type. + * @return {!Entry} The converted entry. + */ + fromClosureLogRecord: function(logRecord, opt_type) { + return new Entry( + normalizeLevel(/** @type {!Logger.Level} */(logRecord.getLevel())), + '[' + logRecord.getLoggerName() + '] ' + logRecord.getMessage(), + logRecord.getMillis(), + opt_type); + } + }, -/** - * @return {{level: string, message: string, timestamp: number, - * type: string}} The JSON representation of this entry. - */ -webdriver.logging.Entry.prototype.toJSON = function() { - return { - 'level': this.level.name, - 'message': this.message, - 'timestamp': this.timestamp, - 'type': this.type - }; -}; - - -/** - * Converts a {@link goog.debug.LogRecord} into a - * {@link webdriver.logging.Entry}. - * @param {!goog.debug.LogRecord} logRecord The record to convert. - * @param {string=} opt_type The log type. - * @return {!webdriver.logging.Entry} The converted entry. - */ -webdriver.logging.Entry.fromClosureLogRecord = function(logRecord, opt_type) { - var closureLevel = logRecord.getLevel(); - var level = webdriver.logging.Level.SEVERE; - - if (closureLevel.value <= webdriver.logging.Level.DEBUG.value) { - level = webdriver.logging.Level.DEBUG; - } else if (closureLevel.value <= webdriver.logging.Level.INFO.value) { - level = webdriver.logging.Level.INFO; - } else if (closureLevel.value <= webdriver.logging.Level.WARNING.value) { - level = webdriver.logging.Level.WARNING; + /** + * @return {{level: string, message: string, timestamp: number, + * type: string}} The JSON representation of this entry. + */ + toJSON: function() { + return { + 'level': this.level.name, + 'message': this.message, + 'timestamp': this.timestamp, + 'type': this.type + }; } - - return new webdriver.logging.Entry( - level, - '[' + logRecord.getLoggerName() + '] ' + logRecord.getMessage(), - logRecord.getMillis(), - opt_type); -}; +}); +exports.Entry = Entry; diff --git a/lib/webdriver/promise.js b/lib/webdriver/promise.js index 59dc609..9347ef1 100644 --- a/lib/webdriver/promise.js +++ b/lib/webdriver/promise.js @@ -1,16 +1,19 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. /** * @license Portions of this code are from the Dojo toolkit, received under the @@ -41,534 +44,1386 @@ */ /** - * @fileoverview A promise implementation based on the CommonJS promise/A and - * promise/B proposals. For more information, see - * http://wiki.commonjs.org/wiki/Promises. + * @fileoverview + * The promise module is centered around the + * {@linkplain webdriver.promise.ControlFlow ControlFlow}, a class that + * coordinates the execution of asynchronous tasks. The ControlFlow allows users + * to focus on the imperative commands for their script without worrying about + * chaining together every single asynchronous action, which can be tedious and + * verbose. APIs may be layered on top of the control flow to read as if they + * were synchronous. For instance, the core + * {@linkplain webdriver.WebDriver WebDriver} API is built on top of the + * control flow, allowing users to write + * + * driver.get('http://www.google.com/ncr'); + * driver.findElement({name: 'q'}).sendKeys('webdriver'); + * driver.findElement({name: 'btnGn'}).click(); + * + * instead of + * + * driver.get('http://www.google.com/ncr') + * .then(function() { + * return driver.findElement({name: 'q'}); + * }) + * .then(function(q) { + * return q.sendKeys('webdriver'); + * }) + * .then(function() { + * return driver.findElement({name: 'btnG'}); + * }) + * .then(function(btnG) { + * return btnG.click(); + * }); + * + * ## Tasks and Task Queues + * + * The control flow is based on the concept of tasks and task queues. Tasks are + * functions that define the basic unit of work for the control flow to execute. + * Each task is scheduled via + * {@link webdriver.promise.ControlFlow#execute() ControlFlow#execute()}, which + * will return a {@link webdriver.promise.Promise Promise} that will be resolved + * with the task's result. + * + * A task queue contains all of the tasks scheduled within a single turn of the + * [JavaScript event loop][JSEL]. The control flow will create a new task queue + * the first time a task is scheduled within an event loop. + * + * var flow = promise.controlFlow(); + * flow.execute(foo); // Creates a new task queue and inserts foo. + * flow.execute(bar); // Inserts bar into the same queue as foo. + * setTimeout(function() { + * flow.execute(baz); // Creates a new task queue and inserts baz. + * }, 0); + * + * Whenever the control flow creates a new task queue, it will automatically + * begin executing tasks in the next available turn of the event loop. This + * execution is scheduled using a "micro-task" timer, such as a (native) + * `Promise.then()` callback. + * + * setTimeout(() => console.log('a')); + * Promise.resolve().then(() => console.log('b')); // A native promise. + * flow.execute(() => console.log('c')); + * Promise.resolve().then(() => console.log('d')); + * setTimeout(() => console.log('fin')); + * // b + * // c + * // d + * // a + * // fin + * + * In the example above, b/c/d is logged before a/fin because native promises + * and this module use "micro-task" timers, which have a higher priority than + * "macro-tasks" like `setTimeout`. + * + * ## Task Execution + * + * Upon creating a task queue, and whenever an exisiting queue completes a task, + * the control flow will schedule a micro-task timer to process any scheduled + * tasks. This ensures no task is ever started within the same turn of the + * JavaScript event loop in which it was scheduled, nor is a task ever started + * within the same turn that another finishes. + * + * When the execution timer fires, a single task will be dequeued and executed. + * There are several important events that may occur while executing a task + * function: + * + * 1. A new task queue is created by a call to + * {@link webdriver.promise.ControlFlow#execute ControlFlow#execute()}. Any + * tasks scheduled within this task queue are considered subtasks of the + * current task. + * 2. The task function throws an error. Any scheduled tasks are immediately + * discarded and the task's promised result (previously returned by + * {@link webdriver.promise.ControlFlow#execute ControlFlow#execute()}) is + * immediately rejected with the thrown error. + * 3. The task function returns sucessfully. + * + * If a task function created a new task queue, the control flow will wait for + * that queue to complete before processing the task result. If the queue + * completes without error, the flow will settle the task's promise with the + * value originaly returned by the task function. On the other hand, if the task + * queue termintes with an error, the task's promise will be rejected with that + * error. + * + * flow.execute(function() { + * flow.execute(() => console.log('a')); + * flow.execute(() => console.log('b')); + * }); + * flow.execute(() => console.log('c')); + * // a + * // b + * // c + * + * ## Promise Integration + * + * In addition to the {@link webdriver.promise.ControlFlow ControlFlow} class, + * the promise module also exports a [Promise/A+] + * {@linkplain webdriver.promise.Promise implementation} that is deeply + * integrated with the ControlFlow. First and foremost, each promise + * {@linkplain webdriver.promise.Promise#then() callback} is scheduled with the + * control flow as a task. As a result, each callback is invoked in its own turn + * of the JavaScript event loop with its own task queue. If any tasks are + * scheduled within a callback, the callback's promised result will not be + * settled until the task queue has completed. + * + * promise.fulfilled().then(function() { + * flow.execute(function() { + * console.log('b'); + * }); + * }).then(() => console.log('a')); + * // b + * // a + * + * ### Scheduling Promise Callbacks + * + * How callbacks are scheduled in the control flow depends on when they are + * attached to the promise. Callbacks attached to a _previously_ resolved + * promise are immediately enqueued as subtasks of the currently running task. + * + * var p = promise.fulfilled(); + * flow.execute(function() { + * flow.execute(() => console.log('A')); + * p.then( () => console.log('B')); + * flow.execute(() => console.log('C')); + * p.then( () => console.log('D')); + * }).then(function() { + * console.log('fin'); + * }); + * // A + * // B + * // C + * // D + * // fin + * + * When a promise is resolved while a task function is on the call stack, any + * callbacks also registered in that stack frame are scheduled as if the promise + * were already resolved: + * + * var d = promise.defer(); + * flow.execute(function() { + * flow.execute( () => console.log('A')); + * d.promise.then(() => console.log('B')); + * flow.execute( () => console.log('C')); + * d.promise.then(() => console.log('D')); + * + * d.fulfill(); + * }).then(function() { + * console.log('fin'); + * }); + * // A + * // B + * // C + * // D + * // fin + * + * If a promise is resolved while a task function is on the call stack, any + * previously registered callbacks (i.e. attached while the task was _not_ on + * the call stack), act as _interrupts_ and are inserted at the front of the + * task queue. If multiple promises are fulfilled, their interrupts are enqueued + * in the order the promises are resolved. + * + * var d1 = promise.defer(); + * d1.promise.then(() => console.log('A')); + * + * var d2 = promise.defer(); + * d2.promise.then(() => console.log('B')); + * + * flow.execute(function() { + * flow.execute(() => console.log('C')); + * flow.execute(() => console.log('D')); + * d1.fulfill(); + * d2.fulfill(); + * }).then(function() { + * console.log('fin'); + * }); + * // A + * // B + * // C + * // D + * // fin + * + * Within a task function (or callback), each step of a promise chain acts as + * an interrupt on the task queue: + * + * var d = promise.defer(); + * flow.execute(function() { + * d.promise. + * then(() => console.log('A')). + * then(() => console.log('B')). + * then(() => console.log('C')). + * then(() => console.log('D')); + * + * flow.execute(() => console.log('E')); + * d.fulfill(); + * }).then(function() { + * console.log('fin'); + * }); + * // A + * // B + * // C + * // D + * // E + * // fin + * + * If there are multiple promise chains derived from a single promise, they are + * processed in the order created: + * + * var d = promise.defer(); + * flow.execute(function() { + * var chain = d.promise.then(() => console.log('A')); + * + * chain.then(() => console.log('B')). + * then(() => console.log('C')); + * + * chain.then(() => console.log('D')). + * then(() => console.log('E')); + * + * flow.execute(() => console.log('F')); + * + * d.fulfill(); + * }).then(function() { + * console.log('fin'); + * }); + * // A + * // B + * // C + * // D + * // E + * // F + * // fin + * + * Even though a subtask's promised result will never resolve while the task + * function is on the stack, it will be treated as a promise resolved within the + * task. In all other scenarios, a task's promise behaves just like a normal + * promise. In the sample below, `C/D` is loggged before `B` because the + * resolution of `subtask1` interrupts the flow of the enclosing task. Within + * the final subtask, `E/F` is logged in order because `subtask1` is a resolved + * promise when that task runs. + * + * flow.execute(function() { + * var subtask1 = flow.execute(() => console.log('A')); + * var subtask2 = flow.execute(() => console.log('B')); + * + * subtask1.then(() => console.log('C')); + * subtask1.then(() => console.log('D')); + * + * flow.execute(function() { + * flow.execute(() => console.log('E')); + * subtask1.then(() => console.log('F')); + * }); + * }).then(function() { + * console.log('fin'); + * }); + * // A + * // C + * // D + * // B + * // E + * // F + * // fin + * + * __Note__: while the ControlFlow will wait for + * {@linkplain webdriver.promise.ControlFlow#execute tasks} and + * {@linkplain webdriver.promise.Promise#then callbacks} to complete, it + * _will not_ wait for unresolved promises created within a task: + * + * flow.execute(function() { + * var p = new promise.Promise(function(fulfill) { + * setTimeout(fulfill, 100); + * }); + * + * p.then(() => console.log('promise resolved!')); + * + * }).then(function() { + * console.log('task complete!'); + * }); + * // task complete! + * // promise resolved! + * + * Finally, consider the following: + * + * var d = promise.defer(); + * d.promise.then(() => console.log('A')); + * d.promise.then(() => console.log('B')); + * + * flow.execute(function() { + * flow.execute( () => console.log('C')); + * d.promise.then(() => console.log('D')); + * + * flow.execute( () => console.log('E')); + * d.promise.then(() => console.log('F')); + * + * d.fulfill(); + * + * flow.execute( () => console.log('G')); + * d.promise.then(() => console.log('H')); + * }).then(function() { + * console.log('fin'); + * }); + * // A + * // B + * // C + * // D + * // E + * // F + * // G + * // H + * // fin + * + * In this example, callbacks are registered on `d.promise` both before and + * during the invocation of the task function. When `d.fulfill()` is called, + * the callbacks registered before the task (`A` & `B`) are registered as + * interrupts. The remaining callbacks were all attached within the task and + * are scheduled in the flow as standard tasks. + * + * ## Generator Support + * + * [Generators][GF] may be scheduled as tasks within a control flow or attached + * as callbacks to a promise. Each time the generator yields a promise, the + * control flow will wait for that promise to settle before executing the next + * iteration of the generator. The yielded promise's fulfilled value will be + * passed back into the generator: + * + * flow.execute(function* () { + * var d = promise.defer(); + * + * setTimeout(() => console.log('...waiting...'), 25); + * setTimeout(() => d.fulfill(123), 50); + * + * console.log('start: ' + Date.now()); + * + * var value = yield d.promise; + * console.log('mid: %d; value = %d', Date.now(), value); + * + * yield promise.delayed(10); + * console.log('end: ' + Date.now()); + * }).then(function() { + * console.log('fin'); + * }); + * // start: 0 + * // ...waiting... + * // mid: 50; value = 123 + * // end: 60 + * // fin + * + * Yielding the result of a promise chain will wait for the entire chain to + * complete: + * + * promise.fulfilled().then(function* () { + * console.log('start: ' + Date.now()); + * + * var value = yield flow. + * execute(() => console.log('A')). + * then( () => console.log('B')). + * then( () => 123); + * + * console.log('mid: %s; value = %d', Date.now(), value); + * + * yield flow.execute(() => console.log('C')); + * }).then(function() { + * console.log('fin'); + * }); + * // start: 0 + * // A + * // B + * // mid: 2; value = 123 + * // C + * // fin + * + * Yielding a _rejected_ promise will cause the rejected value to be thrown + * within the generator function: + * + * flow.execute(function* () { + * console.log('start: ' + Date.now()); + * try { + * yield promise.delayed(10).then(function() { + * throw Error('boom'); + * }); + * } catch (ex) { + * console.log('caught time: ' + Date.now()); + * console.log(ex.message); + * } + * }); + * // start: 0 + * // caught time: 10 + * // boom + * + * # Error Handling + * + * ES6 promises do not require users to handle a promise rejections. This can + * result in subtle bugs as the rejections are silently "swallowed" by the + * Promise class. + * + * Promise.reject(Error('boom')); + * // ... *crickets* ... + * + * Selenium's {@link webdriver.promise promise} module, on the other hand, + * requires that every rejection be explicitly handled. When a + * {@linkplain webdriver.promise.Promise Promise} is rejected and no callbacks + * are defined on that promise, it is considered an _unhandled rejection_ and + * reproted to the active task queue. If the rejection remains unhandled after + * a single turn of the [event loop][JSEL] (scheduled with a micro-task), it + * will propagate up the stack. + * + * ## Error Propagation + * + * If an unhandled rejection occurs within a task function, that task's promised + * result is rejected and all remaining subtasks are discarded: + * + * flow.execute(function() { + * // No callbacks registered on promise -> unhandled rejection + * promise.rejected(Error('boom')); + * flow.execute(function() { console.log('this will never run'); }); + * }).thenCatch(function(e) { + * console.log(e.message); + * }); + * // boom + * + * The promised results for discarded tasks are silently rejected with a + * cancellation error and existing callback chains will never fire. + * + * flow.execute(function() { + * promise.rejected(Error('boom')); + * flow.execute(function() { console.log('a'); }). + * then(function() { console.log('b'); }); + * }).thenCatch(function(e) { + * console.log(e.message); + * }); + * // boom + * + * An unhandled rejection takes precedence over a task function's returned + * result, even if that value is another promise: + * + * flow.execute(function() { + * promise.rejected(Error('boom')); + * return flow.execute(someOtherTask); + * }).thenCatch(function(e) { + * console.log(e.message); + * }); + * // boom + * + * If there are multiple unhandled rejections within a task, they are packaged + * in a {@link webdriver.promise.MultipleUnhandledRejectionError + * MultipleUnhandledRejectionError}, which has an `errors` property that is a + * `Set` of the recorded unhandled rejections: + * + * flow.execute(function() { + * promise.rejected(Error('boom1')); + * promise.rejected(Error('boom2')); + * }).thenCatch(function(ex) { + * console.log(ex instanceof promise.MultipleUnhandledRejectionError); + * for (var e of ex.errors) { + * console.log(e.message); + * } + * }); + * // boom1 + * // boom2 + * + * When a subtask is discarded due to an unreported rejection in its parent + * frame, the existing callbacks on that task will never settle and the + * callbacks will not be invoked. If a new callback is attached ot the subtask + * _after_ it has been discarded, it is handled the same as adding a callback + * to a cancelled promise: the error-callback path is invoked. This behavior is + * intended to handle cases where the user saves a reference to a task promise, + * as illustrated below. + * + * var subTask; + * flow.execute(function() { + * promise.rejected(Error('boom')); + * subTask = flow.execute(function() {}); + * }).thenCatch(function(e) { + * console.log(e.message); + * }).then(function() { + * return subTask.then( + * () => console.log('subtask success!'), + * (e) => console.log('subtask failed:\n' + e)); + * }); + * // boom + * // subtask failed: + * // DiscardedTaskError: Task was discarded due to a previous failure: boom + * + * When a subtask fails, its promised result is treated the same as any other + * promise: it must be handled within one turn of the rejection or the unhandled + * rejection is propagated to the parent task. This means users can catch errors + * from complex flows from the top level task: + * + * flow.execute(function() { + * flow.execute(function() { + * flow.execute(function() { + * throw Error('fail!'); + * }); + * }); + * }).thenCatch(function(e) { + * console.log(e.message); + * }); + * // fail! + * + * ## Unhandled Rejection Events + * + * When an unhandled rejection propagates to the root of the control flow, the + * flow will emit an __uncaughtException__ event. If no listeners are registered + * on the flow, the error will be rethrown to the global error handler: an + * __uncaughtException__ event from the + * [`process`](https://nodejs.org/api/process.html) object in node, or + * `window.onerror` when running in a browser. + * + * Bottom line: you __*must*__ handle rejected promises. + * + * # Promise/A+ Compatibility + * + * This `promise` module is compliant with the [Promise/A+][] specification + * except for sections `2.2.6.1` and `2.2.6.2`: + * + * > + * > - `then` may be called multiple times on the same promise. + * > - If/when `promise` is fulfilled, all respective `onFulfilled` callbacks + * > must execute in the order of their originating calls to `then`. + * > - If/when `promise` is rejected, all respective `onRejected` callbacks + * > must execute in the order of their originating calls to `then`. + * > + * + * Specifically, the conformance tests contains the following scenario (for + * brevity, only the fulfillment version is shown): + * + * var p1 = Promise.resolve(); + * p1.then(function() { + * console.log('A'); + * p1.then(() => console.log('B')); + * }); + * p1.then(() => console.log('C')); + * // A + * // C + * // B + * + * Since the [ControlFlow](#scheduling_callbacks) executes promise callbacks as + * tasks, with this module, the result would be + * + * var p2 = promise.fulfilled(); + * p2.then(function() { + * console.log('A'); + * p2.then(() => console.log('B'); + * }); + * p2.then(() => console.log('C')); + * // A + * // B + * // C + * + * [JSEL]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop + * [GF]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* + * [Promise/A+]: https://promisesaplus.com/ */ -goog.provide('webdriver.promise'); -goog.provide('webdriver.promise.ControlFlow'); -goog.provide('webdriver.promise.ControlFlow.Timer'); -goog.provide('webdriver.promise.Deferred'); -goog.provide('webdriver.promise.Promise'); +goog.module('webdriver.promise'); +goog.module.declareLegacyNamespace(); -goog.require('goog.array'); -goog.require('goog.debug.Error'); -goog.require('goog.object'); -goog.require('webdriver.EventEmitter'); -goog.require('webdriver.stacktrace.Snapshot'); +var Arrays = goog.require('goog.array'); +var asserts = goog.require('goog.asserts'); +var asyncRun = goog.require('goog.async.run'); +var throwException = goog.require('goog.async.throwException'); +var DebugError = goog.require('goog.debug.Error'); +var log = goog.require('goog.log'); +var Objects = goog.require('goog.object'); +var EventEmitter = goog.require('webdriver.EventEmitter'); +var stacktrace = goog.require('webdriver.stacktrace'); /** - * 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. - * - *

        This class is based on the Promise/A proposal from CommonJS. Additional - * functions are provided for API compatibility with Dojo Deferred objects. - * - * @constructor - * @see http://wiki.commonjs.org/wiki/Promises/A + * @define {boolean} Whether to append traces of {@code then} to rejection + * errors. */ -webdriver.promise.Promise = function() { -}; - +goog.define('webdriver.promise.LONG_STACK_TRACES', false); -/** - * Cancels the computation of this promise's value, rejecting the promise in the - * process. - * @param {*} reason The reason this promise is being cancelled. If not an - * {@code Error}, one will be created using the value's string - * representation. - */ -webdriver.promise.Promise.prototype.cancel = function(reason) { - throw new TypeError('Unimplemented function: "cancel"'); -}; +/** @const */ +var promise = exports; -/** @return {boolean} Whether this promise's value is still being computed. */ -webdriver.promise.Promise.prototype.isPending = function() { - throw new TypeError('Unimplemented function: "isPending"'); -}; +/** @const */ +var LOG = log.getLogger('webdriver.promise'); /** - * Registers listeners for when this instance is resolved. This function most - * overridden by subtypes. - * - * @param {Function=} opt_callback The function to call if this promise is - * successfully resolved. The function should expect a single argument: the - * promise's resolved value. - * @param {Function=} opt_errback The function to call if this promise is - * rejected. The function should expect a single argument: the rejection - * reason. - * @return {!webdriver.promise.Promise} A new promise which will be resolved - * with the result of the invoked callback. + * @param {number} level What level of verbosity to log with. + * @param {(string|function(this: T): string)} loggable The message to log. + * @param {T=} opt_self The object in whose context to run the loggable + * function. + * @template T */ -webdriver.promise.Promise.prototype.then = function( - opt_callback, opt_errback) { - throw new TypeError('Unimplemented function: "then"'); -}; +function vlog(level, loggable, opt_self) { + var logLevel = log.Level.FINE; + if (level > 1) { + logLevel = log.Level.FINEST; + } else if (level > 0) { + logLevel = log.Level.FINER; + } + + if (typeof loggable === 'function') { + loggable = loggable.bind(opt_self); + } + + log.log(LOG, logLevel, loggable); +} /** - * Registers a listener for when this promise is rejected. This is synonymous - * with the {@code 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);
        - *   });
        - * 
        - * - * @param {!Function} errback The function to call if this promise is - * rejected. The function should expect a single argument: the rejection - * reason. - * @return {!webdriver.promise.Promise} A new promise which will be resolved - * with the result of the invoked callback. + * Generates an error to capture the current stack trace. + * @param {string} name Error name for this stack trace. + * @param {string} msg Message to record. + * @param {!Function} topFn The function that should appear at the top of the + * stack; only applicable in V8. + * @return {!Error} The generated error. */ -webdriver.promise.Promise.prototype.thenCatch = function(errback) { - return this.then(null, errback); +promise.captureStackTrace = function(name, msg, topFn) { + var e = Error(msg); + e.name = name; + if (Error.captureStackTrace) { + Error.captureStackTrace(e, topFn); + } else { + var stack = stacktrace.getStack(e); + e.stack = e.toString(); + if (stack) { + e.stack += '\n' + stack; + } + } + return e; }; /** - * 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 {@code finally} clause in a synchronous API: - *
        
        - *   // Synchronous API:
        - *   try {
        - *     doSynchronousWork();
        - *   } finally {
        - *     cleanUp();
        - *   }
        - *
        - *   // Asynchronous promise API:
        - *   doAsynchronousWork().thenFinally(cleanUp);
        - * 
        - * - * Note: similar to the {@code 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
        - *       });
        - * 
        - * + * Error used when the computation of a promise is cancelled. * - * @param callback - * @returns {!webdriver.promise.Promise} + * @unrestricted */ -webdriver.promise.Promise.prototype.thenFinally = function(callback) { - return this.then(callback, callback); -}; +promise.CancellationError = goog.defineClass(DebugError, { + /** + * @param {string=} opt_msg The cancellation message. + */ + constructor: function(opt_msg) { + promise.CancellationError.base(this, 'constructor', opt_msg); + + /** @override */ + this.name = 'CancellationError'; + + /** @private {boolean} */ + this.silent_ = false; + }, + + statics: { + /** + * Wraps the given error in a CancellationError. + * + * @param {*} error The error to wrap. + * @param {string=} opt_msg The prefix message to use. + * @return {!promise.CancellationError} A cancellation error. + */ + wrap: function(error, opt_msg) { + var message; + if (error instanceof promise.CancellationError) { + return new promise.CancellationError( + opt_msg ? (opt_msg + ': ' + error.message) : error.message); + } else if (opt_msg) { + message = opt_msg; + if (error) { + message += ': ' + error; + } + return new promise.CancellationError(message); + } + if (error) { + message = error + ''; + } + return new promise.CancellationError(message); + } + } +}); /** - * Registers a function to be invoked when this promise is successfully - * resolved. This function is provided for backwards compatibility with the - * Dojo Deferred API. - * - * @param {Function} callback The function to call if this promise is - * successfully resolved. The function should expect a single argument: the - * promise's resolved value. - * @param {!Object=} opt_self The object which |this| should refer to when the - * function is invoked. - * @return {!webdriver.promise.Promise} A new promise which will be resolved - * with the result of the invoked callback. - * @deprecated Use {@link #then()} instead. + * Error used to cancel tasks when a control flow is reset. + * @unrestricted + * @final */ -webdriver.promise.Promise.prototype.addCallback = function(callback, opt_self) { - return this.then(goog.bind(callback, opt_self)); -}; +var FlowResetError = goog.defineClass(promise.CancellationError, { + constructor: function() { + FlowResetError.base(this, 'constructor', 'ControlFlow was reset'); + + /** @override */ + this.name = 'FlowResetError'; + + this.silent_ = true; + } +}); /** - * Registers a function to be invoked when this promise is rejected. - * This function is provided for backwards compatibility with the - * Dojo Deferred API. - * - * @param {Function} errback The function to call if this promise is - * rejected. The function should expect a single argument: the rejection - * reason. - * @param {!Object=} opt_self The object which |this| should refer to when the - * function is invoked. - * @return {!webdriver.promise.Promise} A new promise which will be resolved - * with the result of the invoked callback. - * @deprecated Use {@link #thenCatch()} instead. + * Error used to cancel tasks that have been discarded due to an uncaught error + * reported earlier in the control flow. + * @unrestricted + * @final */ -webdriver.promise.Promise.prototype.addErrback = function(errback, opt_self) { - return this.thenCatch(goog.bind(errback, opt_self)); -}; +var DiscardedTaskError = goog.defineClass(promise.CancellationError, { + /** @param {*} error The original error. */ + constructor: function(error) { + if (error instanceof DiscardedTaskError) { + return /** @type {!DiscardedTaskError} */(error); + } + + var msg = ''; + if (error) { + msg = ': ' + (typeof error.message === 'string' ? error.message : error); + } + + DiscardedTaskError.base(this, 'constructor', + 'Task was discarded due to a previous failure' + msg); + + /** @override */ + this.name = 'DiscardedTaskError'; + this.silent_ = true; + } +}); /** - * Registers a function to be invoked when this promise is either rejected or - * resolved. This function is provided for backwards compatibility with the - * Dojo Deferred API. - * - * @param {Function} callback The function to call when this promise is - * either resolved or rejected. The function should expect a single - * argument: the resolved value or rejection error. - * @param {!Object=} opt_self The object which |this| should refer to when the - * function is invoked. - * @return {!webdriver.promise.Promise} A new promise which will be resolved - * with the result of the invoked callback. - * @deprecated Use {@link #thenFinally()} instead. + * Error used when there are multiple unhandled promise rejections detected + * within a task or callback. + * + * @unrestricted + * @final */ -webdriver.promise.Promise.prototype.addBoth = function(callback, opt_self) { - return this.thenFinally(goog.bind(callback, opt_self)); -}; +promise.MultipleUnhandledRejectionError = goog.defineClass(DebugError, { + /** + * @param {!(Set<*>)} errors The errors to report. + */ + constructor: function(errors) { + promise.MultipleUnhandledRejectionError.base( + this, 'constructor', 'Multiple unhandled promise rejections reported'); + + /** @override */ + this.name = 'MultipleUnhandledRejectionError'; + + /** @type {!Set<*>} */ + this.errors = errors; + } +}); /** - * An alias for {@code webdriver.promise.Promise.prototype.then} that permits - * the scope of the invoked function to be specified. This function is provided - * for backwards compatibility with the Dojo Deferred API. - * - * @param {Function} callback The function to call if this promise is - * successfully resolved. The function should expect a single argument: the - * promise's resolved value. - * @param {Function} errback The function to call if this promise is - * rejected. The function should expect a single argument: the rejection - * reason. - * @param {!Object=} opt_self The object which |this| should refer to when the - * function is invoked. - * @return {!webdriver.promise.Promise} A new promise which will be resolved - * with the result of the invoked callback. - * @deprecated Use {@link #then()} instead. + * Property used to flag constructor's as implementing the Thenable interface + * for runtime type checking. + * @type {string} + * @const */ -webdriver.promise.Promise.prototype.addCallbacks = function( - callback, errback, opt_self) { - return this.then(goog.bind(callback, opt_self), - goog.bind(errback, opt_self)); -}; - +var IMPLEMENTED_BY_PROP = '$webdriver_Thenable'; /** - * 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 {@code promise} property that may be returned to consumers for - * registering callbacks, reserving the ability to resolve the deferred to the - * producer. - * - *

        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 - * {@link webdriver.promise.ControlFlow} as an unhandled failure. + * Thenable is a promise-like object with a {@code then} method which may be + * used to schedule callbacks on a promised value. * - *

        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. - * - * @param {Function=} opt_canceller Function to call when cancelling the - * computation of this instance's value. - * @param {webdriver.promise.ControlFlow=} opt_flow The control flow - * this instance was created under. This should only be provided during - * unit tests. - * @constructor - * @extends {webdriver.promise.Promise} + * @interface + * @extends {IThenable} + * @template T */ -webdriver.promise.Deferred = function(opt_canceller, opt_flow) { - /* NOTE: This class's implementation diverges from the prototypical style - * used in the rest of the atoms library. This was done intentionally to - * protect the internal Deferred state from consumers, as outlined by - * http://wiki.commonjs.org/wiki/Promises - */ - goog.base(this); - - var flow = opt_flow || webdriver.promise.controlFlow(); +promise.Thenable = goog.defineClass(null, { + statics: { + /** + * Adds a property to a class prototype to allow runtime checks of whether + * instances of that class implement the Thenable interface. This function + * will also ensure the prototype's {@code then} function is exported from + * compiled code. + * @param {function(new: promise.Thenable, ...?)} ctor The + * constructor whose prototype to modify. + */ + addImplementation: function(ctor) { + // Based on goog.promise.Thenable.isImplementation. + ctor.prototype['then'] = ctor.prototype.then; + try { + // Old IE7 does not support defineProperty; IE8 only supports it for + // DOM elements. + Object.defineProperty( + ctor.prototype, + IMPLEMENTED_BY_PROP, + {'value': true, 'enumerable': false}); + } catch (ex) { + ctor.prototype[IMPLEMENTED_BY_PROP] = true; + } + }, + + /** + * Checks if an object has been tagged for implementing the Thenable + * interface as defined by + * {@link webdriver.promise.Thenable.addImplementation}. + * @param {*} object The object to test. + * @return {boolean} Whether the object is an implementation of the Thenable + * interface. + */ + isImplementation: function(object) { + // Based on goog.promise.Thenable.isImplementation. + if (!object) { + return false; + } + try { + return !!object[IMPLEMENTED_BY_PROP]; + } catch (e) { + return false; // Property access seems to be forbidden. + } + } + }, /** - * The listeners registered with this Deferred. Each element in the list will - * be a 3-tuple of the callback function, errback function, and the - * corresponding deferred object. - * @type {!Array.} + * Cancels the computation of this promise's value, rejecting the promise in + * the process. This method is a no-op if the promise has already been + * resolved. + * + * @param {(string|promise.CancellationError)=} opt_reason The reason this + * promise is being cancelled. */ - var listeners = []; + cancel: function(opt_reason) {}, - /** - * Whether this Deferred's resolution was ever handled by a listener. - * If the Deferred is rejected and its value is not handled by a listener - * before the next turn of the event loop, the error will be passed to the - * global error handler. - * @type {boolean} - */ - var handled = false; + /** @return {boolean} Whether this promise's value is still being computed. */ + isPending: function() {}, /** - * Key for the timeout used to delay reproting an unhandled rejection to the - * parent {@link webdriver.promise.ControlFlow}. - * @type {?number} + * Registers listeners for when this instance is resolved. + * + * @param {?(function(T): (R|IThenable))=} opt_callback The + * function to call if this promise is successfully resolved. The function + * should expect a single argument: the promise's resolved value. + * @param {?(function(*): (R|IThenable))=} opt_errback + * The function to call if this promise is rejected. The function should + * expect a single argument: the rejection reason. + * @return {!promise.Promise} A new promise which will be + * resolved with the result of the invoked callback. + * @template R */ - var pendingRejectionKey = null; + then: function(opt_callback, opt_errback) {}, /** - * This Deferred's current state. - * @type {!webdriver.promise.Deferred.State_} + * Registers a listener for when this promise is rejected. This is synonymous + * with the {@code 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); + * }); + * + * @param {function(*): (R|IThenable)} errback The + * function to call if this promise is rejected. The function should + * expect a single argument: the rejection reason. + * @return {!promise.Promise} A new promise which will be + * resolved with the result of the invoked callback. + * @template R */ - var state = webdriver.promise.Deferred.State_.PENDING; + thenCatch: function(errback) {}, /** - * This Deferred's resolved value; set when the state transitions from - * {@code webdriver.promise.Deferred.State_.PENDING}. - * @type {*} + * 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 {@code finally} clause in a synchronous API: + * + * // Synchronous API: + * try { + * doSynchronousWork(); + * } finally { + * cleanUp(); + * } + * + * // Asynchronous promise API: + * doAsynchronousWork().thenFinally(cleanUp); + * + * __Note:__ similar to the {@code 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 + * } + * + * promise.rejected(Error('one')) + * .thenFinally(function() { + * throw Error('two'); // Hides Error: one + * }); + * + * @param {function(): (R|IThenable)} callback The function + * to call when this promise is resolved. + * @return {!promise.Promise} A promise that will be fulfilled + * with the callback result. + * @template R */ - var value; + thenFinally: function(callback) {} +}); - /** @return {boolean} Whether this promise's value is still pending. */ - function isPending() { - return state == webdriver.promise.Deferred.State_.PENDING; - } - /** - * Removes all of the listeners previously registered on this deferred. - * @throws {Error} If this deferred has already been resolved. - */ - function removeAll() { - listeners = []; - } +/** + * @enum {string} + */ +var PromiseState = { + PENDING: 'pending', + BLOCKED: 'blocked', + REJECTED: 'rejected', + FULFILLED: 'fulfilled' +}; + + +/** + * Internal symbol used to store a cancellation handler for + * {@link promise.Promise} objects. This is an internal implementation detail + * used by the {@link TaskQueue} class to monitor for when a promise is + * cancelled without generating an extra promise via then(). + */ +var CANCEL_HANDLER_SYMBOL = Symbol('on cancel'); + + +/** + * Represents the eventual value of a completed operation. Each promise may be + * in one of three states: pending, fulfilled, or rejected. Each promise starts + * in the pending state and may make a single transition to either a + * fulfilled or rejected state, at which point the promise is considered + * resolved. + * + * @implements {promise.Thenable} + * @template T + * @see http://promises-aplus.github.io/promises-spec/ + * @unrestricted // For using CANCEL_HANDLER_SYMBOL. + */ +promise.Promise = goog.defineClass(null, { /** - * Resolves this deferred. If the new value is a promise, this function will - * wait for it to be resolved before notifying the registered listeners. - * @param {!webdriver.promise.Deferred.State_} newState The deferred's new - * state. - * @param {*} newValue The deferred's new value. + * @param {function( + * function((T|IThenable|Thenable)=), + * function(*=))} resolver + * Function that is invoked immediately to begin computation of this + * promise's value. The function should accept a pair of callback + * functions, one for fulfilling the promise and another for rejecting it. + * @param {promise.ControlFlow=} opt_flow The control flow + * this instance was created under. Defaults to the currently active flow. */ - function resolve(newState, newValue) { - if (webdriver.promise.Deferred.State_.PENDING !== state) { - return; + constructor: function(resolver, opt_flow) { + goog.getUid(this); + + /** @private {!promise.ControlFlow} */ + this.flow_ = opt_flow || promise.controlFlow(); + + /** @private {Error} */ + this.stack_ = null; + if (promise.LONG_STACK_TRACES) { + this.stack_ = promise.captureStackTrace( + 'Promise', 'new', promise.Promise); } - state = webdriver.promise.Deferred.State_.BLOCKED; + /** @private {promise.Promise} */ + this.parent_ = null; - if (webdriver.promise.isPromise(newValue) && newValue !== self) { - var onFulfill = goog.partial(notifyAll, newState); - var onReject = goog.partial( - notifyAll, webdriver.promise.Deferred.State_.REJECTED); - if (newValue instanceof webdriver.promise.Deferred) { - newValue.then(onFulfill, onReject); - } else { - webdriver.promise.asap(newValue, onFulfill, onReject); - } + /** @private {Array} */ + this.callbacks_ = null; - } else { - notifyAll(newState, newValue); + /** @private {PromiseState} */ + this.state_ = PromiseState.PENDING; + + /** @private {boolean} */ + this.handled_ = false; + + /** @private {*} */ + this.value_ = undefined; + + /** @private {TaskQueue} */ + this.queue_ = null; + + /** @private {(function(promise.CancellationError)|null)} */ + this[CANCEL_HANDLER_SYMBOL] = null; + + try { + var self = this; + resolver(function(value) { + self.resolve_(PromiseState.FULFILLED, value); + }, function(reason) { + self.resolve_(PromiseState.REJECTED, reason); + }); + } catch (ex) { + this.resolve_(PromiseState.REJECTED, ex); } - } + }, + + /** @override */ + toString: function() { + return 'Promise::' + goog.getUid(this) + + ' {[[PromiseStatus]]: "' + this.state_ + '"}'; + }, /** - * Notifies all of the listeners registered with this Deferred that its state - * has changed. - * @param {!webdriver.promise.Deferred.State_} newState The deferred's new - * state. - * @param {*} newValue The deferred's new value. + * Resolves this promise. If the new value is itself a promise, this function + * will wait for it to be resolved before notifying the registered listeners. + * @param {PromiseState} newState The promise's new state. + * @param {*} newValue The promise's new value. + * @throws {TypeError} If {@code newValue === this}. + * @private */ - function notifyAll(newState, newValue) { - if (newState === webdriver.promise.Deferred.State_.REJECTED && - // We cannot check instanceof Error since the object may have been - // created in a different JS context. - goog.isObject(newValue) && goog.isString(newValue.message)) { - newValue = flow.annotateError(/** @type {!Error} */(newValue)); + resolve_: function(newState, newValue) { + if (PromiseState.PENDING !== this.state_) { + return; + } + + if (newValue === this) { + // See promise a+, 2.3.1 + // http://promises-aplus.github.io/promises-spec/#point-48 + newValue = new TypeError('A promise may not resolve to itself'); + newState = PromiseState.REJECTED; } - state = newState; - value = newValue; - while (listeners.length) { - notify(listeners.shift()); + this.parent_ = null; + this.state_ = PromiseState.BLOCKED; + + if (newState !== PromiseState.REJECTED) { + if (promise.Thenable.isImplementation(newValue)) { + // 2.3.2 + newValue = /** @type {!promise.Thenable} */(newValue); + newValue.then( + this.unblockAndResolve_.bind(this, PromiseState.FULFILLED), + this.unblockAndResolve_.bind(this, PromiseState.REJECTED)); + return; + + } else if (goog.isObject(newValue)) { + // 2.3.3 + + try { + // 2.3.3.1 + var then = newValue['then']; + } catch (e) { + // 2.3.3.2 + this.state_ = PromiseState.REJECTED; + this.value_ = e; + this.scheduleNotifications_(); + return; + } + + // NB: goog.isFunction is loose and will accept instanceof Function. + if (typeof then === 'function') { + // 2.3.3.3 + this.invokeThen_(newValue, then); + return; + } + } } - if (!handled && state == webdriver.promise.Deferred.State_.REJECTED) { - pendingRejectionKey = propagateError(value); + if (newState === PromiseState.REJECTED && + isError(newValue) && newValue.stack && this.stack_) { + newValue.stack += '\nFrom: ' + (this.stack_.stack || this.stack_); } - } - /** - * Propagates an unhandled rejection to the parent ControlFlow in a - * future turn of the JavaScript event loop. - * @param {*} error The error value to report. - * @return {number} The key for the registered timeout. - */ - function propagateError(error) { - flow.pendingRejections_ += 1; - return flow.timer.setTimeout(function() { - flow.pendingRejections_ -= 1; - flow.abortFrame_(error); - }, 0); - } + // 2.3.3.4 and 2.3.4 + this.state_ = newState; + this.value_ = newValue; + this.scheduleNotifications_(); + }, /** - * Notifies a single listener of this Deferred's change in state. - * @param {!webdriver.promise.Deferred.Listener_} listener The listener to - * notify. + * Invokes a thenable's "then" method according to 2.3.3.3 of the promise + * A+ spec. + * @param {!Object} x The thenable object. + * @param {!Function} then The "then" function to invoke. + * @private */ - function notify(listener) { - var func = state == webdriver.promise.Deferred.State_.RESOLVED ? - listener.callback : listener.errback; - if (func) { - flow.runInNewFrame_(goog.partial(func, value), - listener.fulfill, listener.reject); - } else if (state == webdriver.promise.Deferred.State_.REJECTED) { - listener.reject(value); - } else { - listener.fulfill(value); + invokeThen_: function(x, then) { + var called = false; + var self = this; + + var resolvePromise = function(value) { + if (!called) { // 2.3.3.3.3 + called = true; + // 2.3.3.3.1 + self.unblockAndResolve_(PromiseState.FULFILLED, value); + } + }; + + var rejectPromise = function(reason) { + if (!called) { // 2.3.3.3.3 + called = true; + // 2.3.3.3.2 + self.unblockAndResolve_(PromiseState.REJECTED, reason); + } + }; + + try { + // 2.3.3.3 + then.call(x, resolvePromise, rejectPromise); + } catch (e) { + // 2.3.3.3.4.2 + rejectPromise(e); } - } + }, /** - * The consumer promise for this instance. Provides protected access to the - * callback registering functions. - * @type {!webdriver.promise.Promise} + * @param {PromiseState} newState The promise's new state. + * @param {*} newValue The promise's new value. + * @private */ - var promise = new webdriver.promise.Promise(); + unblockAndResolve_: function(newState, newValue) { + if (this.state_ === PromiseState.BLOCKED) { + this.state_ = PromiseState.PENDING; + this.resolve_(newState, newValue); + } + }, /** - * Registers a callback on this Deferred. - * @param {Function=} opt_callback The callback. - * @param {Function=} opt_errback The errback. - * @return {!webdriver.promise.Promise} A new promise representing the result - * of the callback. - * @see webdriver.promise.Promise#then + * @private */ - function then(opt_callback, opt_errback) { - // Avoid unnecessary allocations if we weren't given any callback functions. - if (!opt_callback && !opt_errback) { - return promise; - } - - // The moment a listener is registered, we consider this deferred to be - // handled; the callback must handle any rejection errors. - handled = true; - if (pendingRejectionKey) { - flow.pendingRejections_ -= 1; - flow.timer.clearTimeout(pendingRejectionKey); - } - - var deferred = new webdriver.promise.Deferred(cancel, flow); - var listener = { - callback: opt_callback, - errback: opt_errback, - fulfill: deferred.fulfill, - reject: deferred.reject - }; + scheduleNotifications_: function() { + vlog(2, () => this + ' scheduling notifications', this); + + this[CANCEL_HANDLER_SYMBOL] = null; + if (this.value_ instanceof promise.CancellationError + && this.value_.silent_) { + this.callbacks_ = null; + } + + if (!this.queue_) { + this.queue_ = this.flow_.getActiveQueue_(); + } + + if (!this.handled_ && + this.state_ === PromiseState.REJECTED && + !(this.value_ instanceof promise.CancellationError)) { + this.queue_.addUnhandledRejection(this); + } + this.queue_.scheduleCallbacks(this); + }, - if (state == webdriver.promise.Deferred.State_.PENDING || - state == webdriver.promise.Deferred.State_.BLOCKED) { - listeners.push(listener); + /** @override */ + cancel: function(opt_reason) { + if (!canCancel(this)) { + return; + } + + if (this.parent_ && canCancel(this.parent_)) { + this.parent_.cancel(opt_reason); } else { - notify(listener); + var reason = promise.CancellationError.wrap(opt_reason); + if (this[CANCEL_HANDLER_SYMBOL]) { + this[CANCEL_HANDLER_SYMBOL](reason); + this[CANCEL_HANDLER_SYMBOL] = null; + } + + if (this.state_ === PromiseState.BLOCKED) { + this.unblockAndResolve_(PromiseState.REJECTED, reason); + } else { + this.resolve_(PromiseState.REJECTED, reason); + } } - return deferred.promise; - } + function canCancel(promise) { + return promise.state_ === PromiseState.PENDING + || promise.state_ === PromiseState.BLOCKED; + } + }, + + /** @override */ + isPending: function() { + return this.state_ === PromiseState.PENDING; + }, + + /** @override */ + then: function(opt_callback, opt_errback) { + return this.addCallback_( + opt_callback, opt_errback, 'then', promise.Promise.prototype.then); + }, + + /** @override */ + thenCatch: function(errback) { + return this.addCallback_( + null, errback, 'thenCatch', promise.Promise.prototype.thenCatch); + }, - var self = this; + /** @override */ + thenFinally: function(callback) { + var error; + var mustThrow = false; + return this.then(function() { + return callback(); + }, function(err) { + error = err; + mustThrow = true; + return callback(); + }).then(function() { + if (mustThrow) { + throw error; + } + }); + }, /** - * Resolves this promise with the given value. If the value is itself a - * promise and not a reference to this deferred, this instance will wait for - * it before resolving. - * @param {*=} opt_value The resolved value. + * Registers a new callback with this promise + * @param {(function(T): (R|IThenable)|null|undefined)} callback The + * fulfillment callback. + * @param {(function(*): (R|IThenable)|null|undefined)} errback The + * rejection callback. + * @param {string} name The callback name. + * @param {!Function} fn The function to use as the top of the stack when + * recording the callback's creation point. + * @return {!promise.Promise} A new promise which will be resolved with the + * esult of the invoked callback. + * @template R + * @private */ - function fulfill(opt_value) { - resolve(webdriver.promise.Deferred.State_.RESOLVED, opt_value); - } + addCallback_: function(callback, errback, name, fn) { + if (!goog.isFunction(callback) && !goog.isFunction(errback)) { + return this; + } + + this.handled_ = true; + if (this.queue_) { + this.queue_.clearUnhandledRejection(this); + } + + var cb = new Task( + this.flow_, + this.invokeCallback_.bind(this, callback, errback), + name, + promise.LONG_STACK_TRACES ? {name: 'Promise', top: fn} : undefined); + cb.promise.parent_ = this; + + if (this.state_ !== PromiseState.PENDING && + this.state_ !== PromiseState.BLOCKED) { + this.flow_.getActiveQueue_().enqueue(cb); + } else { + if (!this.callbacks_) { + this.callbacks_ = []; + } + this.callbacks_.push(cb); + cb.isVolatile = true; + this.flow_.getActiveQueue_().enqueue(cb); + } + + return cb.promise; + }, /** - * Rejects this promise. If the error is itself a promise, this instance will - * be chained to it and be rejected with the error's resolved value. - * @param {*=} opt_error The rejection reason, typically either a - * {@code Error} or a {@code string}. + * Invokes a callback function attached to this promise. + * @param {(function(T): (R|IThenable)|null|undefined)} callback The + * fulfillment callback. + * @param {(function(*): (R|IThenable)|null|undefined)} errback The + * rejection callback. + * @template R + * @private */ - function reject(opt_error) { - resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error); + invokeCallback_: function(callback, errback) { + var callbackFn = callback; + if (this.state_ === PromiseState.REJECTED) { + callbackFn = errback; + } + + if (goog.isFunction(callbackFn)) { + if (promise.isGenerator(callbackFn)) { + return promise.consume(callbackFn, null, this.value_); + } + return callbackFn(this.value_); + } else if (this.state_ === PromiseState.REJECTED) { + throw this.value_; + } else { + return this.value_; + } } +}); +promise.Thenable.addImplementation(promise.Promise); + +/** + * 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 {@code promise} property that may be returned to consumers for + * registering callbacks, reserving the ability to resolve the deferred to the + * producer. + * + * 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 + * {@link webdriver.promise.ControlFlow} as an unhandled failure. + * + * @implements {promise.Thenable} + * @template T + */ +promise.Deferred = goog.defineClass(null, { /** - * Attempts to cancel the computation of this instance's value. This attempt - * will silently fail if this instance has already resolved. - * @param {*=} opt_reason The reason for cancelling this promise. + * @param {promise.ControlFlow=} opt_flow The control flow this instance was + * created under. This should only be provided during unit tests. */ - function cancel(opt_reason) { - if (!isPending()) { - return; - } + constructor: function(opt_flow) { + var fulfill, reject; - if (opt_canceller) { - opt_reason = opt_canceller(opt_reason) || opt_reason; - } + /** @type {!promise.Promise} */ + this.promise = new promise.Promise(function(f, r) { + fulfill = f; + reject = r; + }, opt_flow); - reject(opt_reason); - } + var self = this; + var checkNotSelf = function(value) { + if (value === self) { + throw new TypeError('May not resolve a Deferred with itself'); + } + }; - this.promise = promise; - this.promise.then = this.then = then; - this.promise.cancel = this.cancel = cancel; - this.promise.isPending = this.isPending = isPending; - this.fulfill = fulfill; - this.reject = this.errback = reject; - - // Only expose this function to our internal classes. - // TODO: find a cleaner way of handling this. - if (this instanceof webdriver.promise.Task_) { - this.removeAll = removeAll; - } + /** + * Resolves this deferred with the given value. It is safe to call this as a + * normal function (with no bound "this"). + * @param {(T|IThenable|Thenable)=} opt_value The fulfilled value. + */ + this.fulfill = function(opt_value) { + checkNotSelf(opt_value); + fulfill(opt_value); + }; - // Export symbols necessary for the contract on this object to work in - // compiled mode. - goog.exportProperty(this, 'then', this.then); - goog.exportProperty(this, 'cancel', cancel); - goog.exportProperty(this, 'fulfill', fulfill); - goog.exportProperty(this, 'reject', reject); - goog.exportProperty(this, 'isPending', isPending); - goog.exportProperty(this, 'promise', this.promise); - goog.exportProperty(this.promise, 'then', this.then); - goog.exportProperty(this.promise, 'cancel', cancel); - goog.exportProperty(this.promise, 'isPending', isPending); -}; -goog.inherits(webdriver.promise.Deferred, webdriver.promise.Promise); + /** + * Rejects this promise with the given reason. It is safe to call this as a + * normal function (with no bound "this"). + * @param {*=} opt_reason The rejection reason. + */ + this.reject = function(opt_reason) { + checkNotSelf(opt_reason); + reject(opt_reason); + }; + }, + /** @override */ + isPending: function() { + return this.promise.isPending(); + }, -/** - * Type definition for a listener registered on a Deferred object. - * @typedef {{callback:(Function|undefined), - * errback:(Function|undefined), - * fulfill: function(*), reject: function(*)}} - * @private - */ -webdriver.promise.Deferred.Listener_; + /** @override */ + cancel: function(opt_reason) { + this.promise.cancel(opt_reason); + }, + + /** + * @override + * @deprecated Use {@code then} from the promise property directly. + */ + then: function(opt_cb, opt_eb) { + return this.promise.then(opt_cb, opt_eb); + }, + /** + * @override + * @deprecated Use {@code thenCatch} from the promise property directly. + */ + thenCatch: function(opt_eb) { + return this.promise.thenCatch(opt_eb); + }, -/** - * The three states a {@link webdriver.promise.Deferred} object may be in. - * @enum {number} - * @private - */ -webdriver.promise.Deferred.State_ = { - REJECTED: -1, - PENDING: 0, - BLOCKED: 1, - RESOLVED: 2 -}; + /** + * @override + * @deprecated Use {@code thenFinally} from the promise property directly. + */ + thenFinally: function(opt_cb) { + return this.promise.thenFinally(opt_cb); + } +}); +promise.Thenable.addImplementation(promise.Deferred); /** @@ -576,16 +1431,15 @@ webdriver.promise.Deferred.State_ = { * instanceof check since the value may originate from another context. * @param {*} value The value to test. * @return {boolean} Whether the value is an error. - * @private */ -webdriver.promise.isError_ = function(value) { +function isError(value) { return value instanceof Error || goog.isObject(value) && - (Object.prototype.toString.call(value) === '[object Error]' || + (goog.isString(value.message) || // A special test for goog.testing.JsUnitException. value.isJsUnitException); -}; +} /** @@ -595,11 +1449,13 @@ webdriver.promise.isError_ = function(value) { * @param {*} value The value to test. * @return {boolean} Whether the value is a promise. */ -webdriver.promise.isPromise = function(value) { +promise.isPromise = function(value) { return !!value && goog.isObject(value) && // Use array notation so the Closure compiler does not obfuscate away our - // contract. - goog.isFunction(value['then']); + // contract. Use typeof rather than goog.isFunction because + // goog.isFunction accepts instanceof Function, which the promise spec + // does not. + typeof value['then'] === 'function'; }; @@ -607,42 +1463,46 @@ webdriver.promise.isPromise = function(value) { * Creates a promise that will be resolved at a set time in the future. * @param {number} ms The amount of time, in milliseconds, to wait before * resolving the promise. - * @return {!webdriver.promise.Promise} The promise. + * @return {!promise.Promise} The promise. */ -webdriver.promise.delayed = function(ms) { - var timer = webdriver.promise.controlFlow().timer; +promise.delayed = function(ms) { var key; - var deferred = new webdriver.promise.Deferred(function() { - timer.clearTimeout(key); + return new promise.Promise(function(fulfill) { + key = setTimeout(function() { + key = null; + fulfill(); + }, ms); + }).thenCatch(function(e) { + clearTimeout(key); + key = null; + throw e; }); - key = timer.setTimeout(deferred.fulfill, ms); - return deferred.promise; }; /** * Creates a new deferred object. - * @param {Function=} opt_canceller Function to call when cancelling the - * computation of this instance's value. - * @return {!webdriver.promise.Deferred} The new deferred object. + * @return {!promise.Deferred} The new deferred object. + * @template T */ -webdriver.promise.defer = function(opt_canceller) { - return new webdriver.promise.Deferred(opt_canceller); +promise.defer = function() { + return new promise.Deferred(); }; /** * Creates a promise that has been resolved with the given value. - * @param {*=} opt_value The resolved value. - * @return {!webdriver.promise.Promise} The resolved promise. + * @param {T=} opt_value The resolved value. + * @return {!promise.Promise} The resolved promise. + * @template T */ -webdriver.promise.fulfilled = function(opt_value) { - if (opt_value instanceof webdriver.promise.Promise) { +promise.fulfilled = function(opt_value) { + if (opt_value instanceof promise.Promise) { return opt_value; } - var deferred = new webdriver.promise.Deferred(); - deferred.fulfill(opt_value); - return deferred.promise; + return new promise.Promise(function(fulfill) { + fulfill(opt_value); + }); }; @@ -650,37 +1510,42 @@ webdriver.promise.fulfilled = function(opt_value) { * Creates a promise that has been rejected with the given reason. * @param {*=} opt_reason The rejection reason; may be any value, but is * usually an Error or a string. - * @return {!webdriver.promise.Promise} The rejected promise. + * @return {!promise.Promise} The rejected promise. + * @template T */ -webdriver.promise.rejected = function(opt_reason) { - var deferred = new webdriver.promise.Deferred(); - deferred.reject(opt_reason); - return deferred.promise; +promise.rejected = function(opt_reason) { + if (opt_reason instanceof promise.Promise) { + return opt_reason; + } + return new promise.Promise(function(_, reject) { + reject(opt_reason); + }); }; /** - * 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 + * Wraps a function that expects a node-style callback as its final + * argument. This callback expects 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. + * The callback will the resolve or reject the returned promise, based on its arguments. * @param {!Function} fn The function to wrap. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the + * @param {...?} var_args The arguments to apply to the function, excluding the + * final callback. + * @return {!promise.Promise} A promise that will be resolved with the * result of the provided function's callback. */ -webdriver.promise.checkedNodeCall = function(fn) { - var deferred = new webdriver.promise.Deferred(function() { - throw Error('This Deferred may not be cancelled'); +promise.checkedNodeCall = function(fn, var_args) { + var args = Arrays.slice(arguments, 1); + return new promise.Promise(function(fulfill, reject) { + try { + args.push(function(error, value) { + error ? reject(error) : fulfill(value); + }); + fn.apply(undefined, args); + } catch (ex) { + reject(ex); + } }); - try { - fn(function(error, value) { - error ? deferred.reject(error) : deferred.fulfill(value); - }); - } catch (ex) { - deferred.reject(ex); - } - return deferred.promise; }; @@ -693,18 +1558,16 @@ webdriver.promise.checkedNodeCall = function(fn) { * resolved successfully. * @param {Function=} opt_errback The function to call when the value is * rejected. - * @return {!webdriver.promise.Promise} A new promise. + * @return {!promise.Promise} A new promise. */ -webdriver.promise.when = function(value, opt_callback, opt_errback) { - if (value instanceof webdriver.promise.Promise) { +promise.when = function(value, opt_callback, opt_errback) { + if (promise.Thenable.isImplementation(value)) { return value.then(opt_callback, opt_errback); } - var deferred = new webdriver.promise.Deferred(); - - webdriver.promise.asap(value, deferred.fulfill, deferred.reject); - - return deferred.then(opt_callback, opt_errback); + return new promise.Promise(function(fulfill, reject) { + promise.asap(value, fulfill, reject); + }).then(opt_callback, opt_errback); }; @@ -718,8 +1581,8 @@ webdriver.promise.when = function(value, opt_callback, opt_errback) { * @param {Function=} opt_errback The function to call when the value is * rejected. */ -webdriver.promise.asap = function(value, callback, opt_errback) { - if (webdriver.promise.isPromise(value)) { +promise.asap = function(value, callback, opt_errback) { + if (promise.isPromise(value)) { value.then(callback, opt_errback); // Maybe a Dojo-like deferred object? @@ -740,38 +1603,37 @@ webdriver.promise.asap = function(value, callback, opt_errback) { * input array's promises are rejected, the returned promise will be rejected * with the same reason. * - * @param {!Array.<(T|!webdriver.promise.Promise.)>} arr An array of + * @param {!Array<(T|!promise.Promise)>} arr An array of * promises to wait on. - * @return {!webdriver.promise.Promise.>} A promise that is + * @return {!promise.Promise>} 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. * @template T */ -webdriver.promise.all = function(arr) { - var n = arr.length; - if (!n) { - return webdriver.promise.fulfilled([]); - } - - var toFulfill = n; - var result = webdriver.promise.defer(); - var values = []; +promise.all = function(arr) { + return new promise.Promise(function(fulfill, reject) { + var n = arr.length; + var values = []; - var onFulfill = function(index, value) { - values[index] = value; - toFulfill--; - if (toFulfill == 0) { - result.fulfill(values); + if (!n) { + fulfill(values); + return; } - }; - for (var i = 0; i < n; ++i) { - webdriver.promise.asap( - arr[i], goog.partial(onFulfill, i), result.reject); - } + var toFulfill = n; + var onFulfilled = function(index, value) { + values[index] = value; + toFulfill--; + if (toFulfill == 0) { + fulfill(values); + } + }; - return result.promise; + for (var i = 0; i < n; ++i) { + promise.asap(arr[i], goog.partial(onFulfilled, i), reject); + } + }); }; @@ -780,27 +1642,53 @@ webdriver.promise.all = function(arr) { * 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 + * 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 + * 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. * - * @param {!(Array.|webdriver.promise.Promise.>)} arr The + * @param {!(Array|promise.Promise>)} arr The * array to iterator over, or a promise that will resolve to said array. - * @param {function(this: SELF, TYPE, number, !Array.): ?} fn The + * @param {function(this: SELF, TYPE, number, !Array): ?} fn The * function to call for each element in the array. This function should * expect three arguments (the element, the index, and the array itself. * @param {SELF=} opt_self The object to be used as the value of 'this' within * {@code fn}. * @template TYPE, SELF */ -webdriver.promise.map = function(arr, fn, opt_self) { - return webdriver.promise.when(arr, function(arr) { - var result = goog.array.map(arr, fn, opt_self); - return webdriver.promise.all(result); +promise.map = function(arr, fn, opt_self) { + return promise.fulfilled(arr).then(function(v) { + goog.asserts.assertNumber(v.length, 'not an array like value'); + var arr = /** @type {!Array} */(v); + return new promise.Promise(function(fulfill, reject) { + var n = arr.length; + var values = new Array(n); + (function processNext(i) { + for (; i < n; i++) { + if (i in arr) { + break; + } + } + if (i >= n) { + fulfill(values); + return; + } + try { + promise.asap( + fn.call(opt_self, arr[i], i, /** @type {!Array} */(arr)), + function(value) { + values[i] = value; + processNext(i + 1); + }, + reject); + } catch (ex) { + reject(ex); + } + })(0); + }); }); }; @@ -809,31 +1697,55 @@ webdriver.promise.map = function(arr, fn, opt_self) { * 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 + * 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 + * 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. * - * @param {!(Array.|webdriver.promise.Promise.>)} arr The + * @param {!(Array|promise.Promise>)} arr The * array to iterator over, or a promise that will resolve to said array. - * @param {function(this: SELF, TYPE, number, !Array.): ( - * boolean|webdriver.promise.Promise.)} fn The function + * @param {function(this: SELF, TYPE, number, !Array): ( + * boolean|promise.Promise)} fn The function * to call for each element in the array. * @param {SELF=} opt_self The object to be used as the value of 'this' within * {@code fn}. * @template TYPE, SELF */ -webdriver.promise.filter = function(arr, fn, opt_self) { - return webdriver.promise.when(arr, function(arr) { - var originalValues = goog.array.clone(arr); - return webdriver.promise.map(arr, fn, opt_self).then(function(include) { - return goog.array.filter(originalValues, function(value, index) { - return include[index]; - }); +promise.filter = function(arr, fn, opt_self) { + return promise.fulfilled(arr).then(function(v) { + goog.asserts.assertNumber(v.length, 'not an array like value'); + var arr = /** @type {!Array} */(v); + return new promise.Promise(function(fulfill, reject) { + var n = arr.length; + var values = []; + var valuesLength = 0; + (function processNext(i) { + for (; i < n; i++) { + if (i in arr) { + break; + } + } + if (i >= n) { + fulfill(values); + return; + } + try { + var value = arr[i]; + var include = fn.call(opt_self, value, i, /** @type {!Array} */(arr)); + promise.asap(include, function(include) { + if (include) { + values[valuesLength++] = value; + } + processNext(i + 1); + }, reject); + } catch (ex) { + reject(ex); + } + })(0); }); }); }; @@ -849,45 +1761,42 @@ webdriver.promise.filter = function(arr, fn, opt_self) { * * Warning: This function makes no checks against objects that contain * cyclical references: - *

        
        - *   var value = {};
        - *   value['self'] = value;
        - *   webdriver.promise.fullyResolved(value);  // Stack overflow.
        - * 
        + * + * var value = {}; + * value['self'] = value; + * promise.fullyResolved(value); // Stack overflow. * * @param {*} value The value to fully resolve. - * @return {!webdriver.promise.Promise} A promise for a fully resolved version + * @return {!promise.Promise} A promise for a fully resolved version * of the input value. */ -webdriver.promise.fullyResolved = function(value) { - if (webdriver.promise.isPromise(value)) { - return webdriver.promise.when(value, webdriver.promise.fullyResolveValue_); +promise.fullyResolved = function(value) { + if (promise.isPromise(value)) { + return promise.when(value, fullyResolveValue); } - return webdriver.promise.fullyResolveValue_(value); + return fullyResolveValue(value); }; /** * @param {*} value The value to fully resolve. If a promise, assumed to * already be resolved. - * @return {!webdriver.promise.Promise} A promise for a fully resolved version + * @return {!promise.Promise} A promise for a fully resolved version * of the input value. - * @private */ -webdriver.promise.fullyResolveValue_ = function(value) { + function fullyResolveValue(value) { switch (goog.typeOf(value)) { case 'array': - return webdriver.promise.fullyResolveKeys_( - /** @type {!Array} */ (value)); + return fullyResolveKeys(/** @type {!Array} */ (value)); case 'object': - if (webdriver.promise.isPromise(value)) { + if (promise.isPromise(value)) { // We get here when the original input value is a promise that // resolves to itself. When the user provides us with such a promise, // trust that it counts as a "fully resolved" value and return it. // Of course, since it's already a promise, we can just return it // to the user instead of wrapping it in another promise. - return /** @type {!webdriver.promise.Promise} */ (value); + return /** @type {!promise.Promise} */ (value); } if (goog.isNumber(value.nodeType) && @@ -895,76 +1804,72 @@ webdriver.promise.fullyResolveValue_ = function(value) { goog.isNumber(value.ownerDocument.nodeType)) { // DOM node; return early to avoid infinite recursion. Should we // only support objects with a certain level of nesting? - return webdriver.promise.fulfilled(value); + return promise.fulfilled(value); } - return webdriver.promise.fullyResolveKeys_( - /** @type {!Object} */ (value)); + return fullyResolveKeys(/** @type {!Object} */ (value)); default: // boolean, function, null, number, string, undefined - return webdriver.promise.fulfilled(value); + return promise.fulfilled(value); } -}; +} /** * @param {!(Array|Object)} obj the object to resolve. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the + * @return {!promise.Promise} A promise that will be resolved with the * input object once all of its values have been fully resolved. - * @private */ -webdriver.promise.fullyResolveKeys_ = function(obj) { + function fullyResolveKeys(obj) { var isArray = goog.isArray(obj); - var numKeys = isArray ? obj.length : goog.object.getCount(obj); + var numKeys = isArray ? obj.length : Objects.getCount(obj); if (!numKeys) { - return webdriver.promise.fulfilled(obj); + return promise.fulfilled(obj); } var numResolved = 0; - var deferred = new webdriver.promise.Deferred(); - - // In pre-IE9, goog.array.forEach will not iterate properly over arrays - // containing undefined values because "index in array" returns false - // when array[index] === undefined (even for x = [undefined, 1]). To get - // around this, we need to use our own forEach implementation. - // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be - // reproduced in IE9 by changing the browser/document modes, it requires an - // actual pre-IE9 browser. Yay, IE! - var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) { - var n = arr.length; - for (var i = 0; i < n; ++i) { - fn.call(null, arr[i], i, arr); - } - }; - - forEachKey(obj, function(partialValue, key) { - var type = goog.typeOf(partialValue); - if (type != 'array' && type != 'object') { - maybeResolveValue(); - return; - } + return new promise.Promise(function(fulfill, reject) { + // In pre-IE9, goog.array.forEach will not iterate properly over arrays + // containing undefined values because "index in array" returns false + // when array[index] === undefined (even for x = [undefined, 1]). To get + // around this, we need to use our own forEach implementation. + // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be + // reproduced in IE9 by changing the browser/document modes, it requires an + // actual pre-IE9 browser. Yay, IE! + var forEachKey = !isArray ? Objects.forEach : function(arr, fn) { + var n = arr.length; + for (var i = 0; i < n; ++i) { + fn.call(null, arr[i], i, arr); + } + }; - webdriver.promise.fullyResolved(partialValue).then( - function(resolvedValue) { - obj[key] = resolvedValue; - maybeResolveValue(); - }, - deferred.reject); - }); + forEachKey(obj, function(partialValue, key) { + var type = goog.typeOf(partialValue); + if (type != 'array' && type != 'object') { + maybeResolveValue(); + return; + } - return deferred.promise; + promise.fullyResolved(partialValue).then( + function(resolvedValue) { + obj[key] = resolvedValue; + maybeResolveValue(); + }, + reject); + }); - function maybeResolveValue() { - if (++numResolved == numKeys) { - deferred.fulfill(obj); + function maybeResolveValue() { + if (++numResolved == numKeys) { + fulfill(obj); + } } - } -}; + }); +} ////////////////////////////////////////////////////////////////////////////// // -// webdriver.promise.ControlFlow +// promise.ControlFlow // ////////////////////////////////////////////////////////////////////////////// @@ -976,1078 +1881,1024 @@ webdriver.promise.fullyResolveKeys_ = function(obj) { * the ordered scheduled, starting each task only once those before it have * completed. * - *

        Each task scheduled within this flow may return a + * Each task scheduled within this flow may return a * {@link webdriver.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. * - *

        Tasks and each callback registered on a {@link webdriver.promise.Deferred} + * Tasks and each callback registered on a {@link webdriver.promise.Promise} * 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 + * frame will take priority over previously scheduled tasks. Furthermore, if any + * of the tasks in the frame fail, 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. * - *

        Each time a ControlFlow empties its task queue, it will fire an - * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Conversely, + * Each time a ControlFlow empties its task queue, it will fire an + * {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event. Conversely, * whenever the flow terminates due to an unhandled error, it will remove all * remaining tasks in its queue and fire an - * {@link 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. - * - * @param {webdriver.promise.ControlFlow.Timer=} opt_timer The timer object - * to use. Should only be set for testing. - * @constructor - * @extends {webdriver.EventEmitter} - */ -webdriver.promise.ControlFlow = function(opt_timer) { - webdriver.EventEmitter.call(this); + * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION + * UNCAUGHT_EXCEPTION} event. If there are no listeners registered with the + * flow, the error will be rethrown to the global error handler. + * + * Refer to the {@link webdriver.promise} module documentation for a detailed + * explanation of how the ControlFlow coordinates task execution. + * + * @final + */ +promise.ControlFlow = goog.defineClass(EventEmitter, { + // TODO: remove this empty comment when the compiler properly handles + // goog.defineClass with a missing constructor comment. + /** @constructor */ + constructor: function() { + promise.ControlFlow.base(this, 'constructor'); + + /** @private {boolean} */ + this.propagateUnhandledRejections_ = true; + + /** @private {TaskQueue} */ + this.activeQueue_ = null; + + /** @private {Set} */ + this.taskQueues_ = null; + + /** + * Micro task that controls shutting down the control flow. Upon shut down, + * the flow will emit an + * {@link 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 = promise.controlFlow().schedule('example', + * function() { return 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); + * + * @private {MicroTask} + */ + this.shutdownTask_ = null; + + /** + * ID for a long running interval used to keep a Node.js process running + * while a control flow's event loop is still working. This is a cheap hack + * required since JS events are only scheduled to run when there is + * _actually_ something to run. When a control flow is waiting on a task, + * there will be nothing in the JS event loop and the process would + * terminate without this. + * + * @private {?number} + */ + this.hold_ = null; + }, /** - * The timer used by this instance. - * @type {webdriver.promise.ControlFlow.Timer} + * Returns a string representation of this control flow, which is its current + * {@link #getSchedule() schedule}, sans task stack traces. + * @return {string} The string representation of this contorl flow. + * @override */ - this.timer = opt_timer || webdriver.promise.ControlFlow.defaultTimer; + toString: function() { + return this.getSchedule(); + }, /** - * A list of recent tasks. Each time a new task is started, or a frame is - * completed, the previously recorded task is removed from this list. If - * there are multiple tasks, task N+1 is considered a sub-task of task - * N. - * @private {!Array.} + * Sets whether any unhandled rejections should propagate up through the + * control flow stack and cause rejections within parent tasks. If error + * propagation is disabled, tasks will not be aborted when an unhandled + * promise rejection is detected, but the rejection _will_ trigger an + * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} + * event. + * + * The default behavior is to propagate all unhandled rejections. _The use + * of this option is highly discouraged._ + * + * @param {boolean} propagate whether to propagate errors. */ - this.history_ = []; -}; -goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter); - - -/** - * @typedef {{clearInterval: function(number), - * clearTimeout: function(number), - * setInterval: function(!Function, number): number, - * setTimeout: function(!Function, number): number}} - */ -webdriver.promise.ControlFlow.Timer; - - -/** - * The default timer object, which uses the global timer functions. - * @type {webdriver.promise.ControlFlow.Timer} - */ -webdriver.promise.ControlFlow.defaultTimer = (function() { - // The default timer functions may be defined as free variables for the - // current context, so do not reference them using "window" or - // "goog.global". Also, we must invoke them in a closure, and not using - // bind(), so we do not get "TypeError: Illegal invocation" (WebKit) or - // "Invalid calling object" (IE) errors. - return { - clearInterval: wrap(clearInterval), - clearTimeout: wrap(clearTimeout), - setInterval: wrap(setInterval), - setTimeout: wrap(setTimeout) - }; - - function wrap(fn) { - return function() { - // Cannot use .call() or .apply() since we do not know which variable - // the function is bound to, and using the wrong one will generate - // an error. - return fn(arguments[0], arguments[1]); - }; - } -})(); - - -/** - * Events that may be emitted by an {@link webdriver.promise.ControlFlow}. - * @enum {string} - */ -webdriver.promise.ControlFlow.EventType = { - - /** Emitted when all tasks have been successfully executed. */ - IDLE: 'idle', - - /** Emitted whenever a new task has been scheduled. */ - SCHEDULE_TASK: 'scheduleTask', + setPropagateUnhandledRejections: function(propagate) { + this.propagateUnhandledRejections_ = propagate; + }, /** - * Emitted whenever a control flow aborts due to an unhandled promise - * rejection. This event will be emitted along with the offending rejection - * reason. Upon emitting this event, the control flow will empty its task - * queue and revert to its initial state. + * @return {boolean} Whether this flow is currently idle. */ - UNCAUGHT_EXCEPTION: 'uncaughtException' -}; - - -/** - * How often, in milliseconds, the event loop should run. - * @type {number} - * @const - */ -webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY = 10; - - -/** - * Tracks the active execution frame for this instance. Lazily initialized - * when the first task is scheduled. - * @private {webdriver.promise.Frame_} - */ -webdriver.promise.ControlFlow.prototype.activeFrame_ = null; - - -/** - * A reference to the frame in which new tasks should be scheduled. If - * {@code null}, 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. - * @private {webdriver.promise.Frame_} - * @see {#runInNewFrame_} - */ -webdriver.promise.ControlFlow.prototype.schedulingFrame_ = null; - - -/** - * Timeout ID set when the flow is about to shutdown without any errors - * being detected. Upon shutting down, the flow will emit an - * {@link 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); - * - * @private {?number} - */ -webdriver.promise.ControlFlow.prototype.shutdownId_ = null; - - -/** - * Interval ID for this instance's event loop. - * @private {?number} - */ -webdriver.promise.ControlFlow.prototype.eventLoopId_ = null; - - -/** - * 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. - * - * @private {number} - */ -webdriver.promise.ControlFlow.prototype.pendingRejections_ = 0; - - -/** - * The number of aborted frames since the last time a task was executed or a - * frame completed successfully. - * @private {number} - */ -webdriver.promise.ControlFlow.prototype.numAbortedFrames_ = 0; - - -/** - * Resets this instance, clearing its queue and removing all event listeners. - */ -webdriver.promise.ControlFlow.prototype.reset = function() { - this.activeFrame_ = null; - this.clearHistory(); - this.removeAllListeners(); - this.cancelShutdown_(); - this.cancelEventLoop_(); -}; - - -/** - * 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. - * @return {!Array.} A summary of this instance's recent task - * activity. - */ -webdriver.promise.ControlFlow.prototype.getHistory = function() { - var pendingTasks = []; - var currentFrame = this.activeFrame_; - while (currentFrame) { - var task = currentFrame.getPendingTask(); - if (task) { - pendingTasks.push(task); - } - // A frame's parent node will always be another frame. - currentFrame = - /** @type {webdriver.promise.Frame_} */ (currentFrame.getParent()); - } - - var fullHistory = goog.array.concat(this.history_, pendingTasks); - return goog.array.map(fullHistory, function(task) { - return task.toString(); - }); -}; - - -/** Clears this instance's task history. */ -webdriver.promise.ControlFlow.prototype.clearHistory = function() { - this.history_ = []; -}; - - -/** - * Removes a completed task from this instance's history record. If any - * tasks remain from aborted frames, those will be removed as well. - * @private - */ -webdriver.promise.ControlFlow.prototype.trimHistory_ = function() { - if (this.numAbortedFrames_) { - goog.array.splice(this.history_, - this.history_.length - this.numAbortedFrames_, - this.numAbortedFrames_); - this.numAbortedFrames_ = 0; - } - this.history_.pop(); -}; - - -/** - * Property used to track whether an error has been annotated by - * {@link webdriver.promise.ControlFlow#annotateError}. - * @private {string} - * @const - */ -webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_ = - 'webdriver_promise_error_'; - - -/** - * 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. - * @param {!(Error|goog.testing.JsUnitException)} e The error to annotate. - * @return {!(Error|goog.testing.JsUnitException)} The annotated error. - */ -webdriver.promise.ControlFlow.prototype.annotateError = function(e) { - if (!!e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_]) { - return e; - } - - var history = this.getHistory(); - if (history.length) { - e = webdriver.stacktrace.format(e); - - /** @type {!Error} */(e).stack += [ - '\n==== async task ====\n', - history.join('\n==== async task ====\n') - ].join(''); - - e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_] = true; - } - - return e; -}; - - -/** - * @return {string} The scheduled tasks still pending with this instance. - */ -webdriver.promise.ControlFlow.prototype.getSchedule = function() { - return this.activeFrame_ ? this.activeFrame_.getRoot().toString() : '[]'; -}; - - -/** - * 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. - * - * @param {!Function} fn The function to call to start the task. If the - * function returns a {@link webdriver.promise.Promise}, this instance - * will wait for it to be resolved before starting the next task. - * @param {string=} opt_description A description of the task. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * the result of the action. - */ -webdriver.promise.ControlFlow.prototype.execute = function( - fn, opt_description) { - this.cancelShutdown_(); - - if (!this.activeFrame_) { - this.activeFrame_ = new webdriver.promise.Frame_(this); - } - - // Trim an extra frame off the generated stack trace for the call to this - // function. - var snapshot = new webdriver.stacktrace.Snapshot(1); - var task = new webdriver.promise.Task_( - this, fn, opt_description || '', snapshot); - var scheduleIn = this.schedulingFrame_ || this.activeFrame_; - scheduleIn.addChild(task); - - this.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK); - - this.scheduleEventLoopStart_(); - return task.promise; -}; - + isIdle: function() { + return !this.shutdownTask_ && (!this.taskQueues_ || !this.taskQueues_.size); + }, -/** - * Inserts a {@code setTimeout} into the command queue. This is equivalent to - * a thread sleep in a synchronous programming language. - * - * @param {number} ms The timeout delay, in milliseconds. - * @param {string=} opt_description A description to accompany the timeout. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * the result of the action. - */ -webdriver.promise.ControlFlow.prototype.timeout = function( - ms, opt_description) { - return this.execute(function() { - return webdriver.promise.delayed(ms); - }, opt_description); -}; + /** + * Resets this instance, clearing its queue and removing all event listeners. + */ + reset: function() { + this.cancelQueues_(new FlowResetError); + this.emit(promise.ControlFlow.EventType.RESET); + this.removeAllListeners(); + this.cancelShutdown_(); + }, + /** + * Generates an annotated string describing the internal state of this control + * flow, including the currently executing as well as pending tasks. If + * {@code opt_includeStackTraces === true}, the string will include the + * stack trace from when each task was scheduled. + * @param {string=} opt_includeStackTraces Whether to include the stack traces + * from when each task was scheduled. Defaults to false. + * @return {string} String representation of this flow's internal state. + */ + getSchedule: function(opt_includeStackTraces) { + var ret = 'ControlFlow::' + goog.getUid(this); + var activeQueue = this.activeQueue_; + if (!this.taskQueues_ || !this.taskQueues_.size) { + return ret; + } + var childIndent = '| '; + for (var q of this.taskQueues_) { + ret += '\n' + printQ(q, childIndent); + } + return ret; -/** - * 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. - * - * @param {!Function} condition The condition function to poll. - * @param {number} timeout How long to wait, in milliseconds, for the condition - * to hold before timing out. - * @param {string=} opt_message An optional error message to include if the - * wait times out; defaults to the empty string. - * @return {!webdriver.promise.Promise} 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. - */ -webdriver.promise.ControlFlow.prototype.wait = function( - condition, timeout, opt_message) { - var sleep = Math.min(timeout, 100); - var self = this; - - return this.execute(function() { - var startTime = goog.now(); - var waitResult = new webdriver.promise.Deferred(); - var waitFrame = self.activeFrame_; - waitFrame.isWaiting = true; - pollCondition(); - return waitResult.promise; - - function pollCondition() { - self.runInNewFrame_(condition, function(value) { - var elapsed = goog.now() - startTime; - if (!!value) { - waitFrame.isWaiting = false; - waitResult.fulfill(value); - } else if (elapsed >= timeout) { - waitResult.reject(new Error((opt_message ? opt_message + '\n' : '') + - 'Wait timed out after ' + elapsed + 'ms')); + function printQ(q, indent) { + var ret = q.toString(); + if (q === activeQueue) { + ret = '(active) ' + ret; + } + var prefix = indent + childIndent; + if (q.pending_) { + if (q.pending_.q.state_ !== TaskQueueState.FINISHED) { + ret += '\n' + prefix + '(pending) ' + q.pending_.task; + ret += '\n' + printQ(q.pending_.q, prefix + childIndent); } else { - self.timer.setTimeout(pollCondition, sleep); + ret += '\n' + prefix + '(blocked) ' + q.pending_.task; } - }, waitResult.reject, true); + } + if (q.interrupts_) { + q.interrupts_.forEach((task) => { + ret += '\n' + prefix + task; + }); + } + if (q.tasks_) { + q.tasks_.forEach((task) => ret += printTask(task, '\n' + prefix)); + } + return indent + ret; } - }, opt_message); -}; - - -/** - * Schedules a task that will wait for another promise to resolve. The resolved - * promise's value will be returned as the task result. - * @param {!webdriver.promise.Promise} promise The promise to wait on. - * @return {!webdriver.promise.Promise} A promise that will resolve when the - * task has completed. - */ -webdriver.promise.ControlFlow.prototype.await = function(promise) { - return this.execute(function() { - return promise; - }); -}; - - -/** - * Schedules the interval for this instance's event loop, if necessary. - * @private - */ -webdriver.promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() { - if (!this.eventLoopId_) { - this.eventLoopId_ = this.timer.setInterval( - goog.bind(this.runEventLoop_, this), - webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY); - } -}; - - -/** - * Cancels the event loop, if necessary. - * @private - */ -webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() { - if (this.eventLoopId_) { - this.timer.clearInterval(this.eventLoopId_); - this.eventLoopId_ = null; - } -}; - - -/** - * 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. - * @private - */ -webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() { - // If we get here and there are pending promise rejections, then those - // promises are queued up to run as soon as this (JS) event loop terminates. - // Short-circuit our loop to give those promises a chance to run. Otherwise, - // we might start a new task only to have it fail because of one of these - // pending rejections. - if (this.pendingRejections_) { - return; - } - // If the flow aborts due to an unhandled exception after we've scheduled - // another turn of the execution loop, we can end up in here with no tasks - // left. This is OK, just quietly return. - if (!this.activeFrame_) { - this.commenceShutdown_(); - return; - } - - var task; - if (this.activeFrame_.getPendingTask() || !(task = this.getNextTask_())) { - // Either the current frame is blocked on a pending task, or we don't have - // a task to finish because we've completed a frame. When completing a - // frame, we must abort the event loop to allow the frame's promise's - // callbacks to execute. - return; - } - - var activeFrame = this.activeFrame_; - activeFrame.setPendingTask(task); - var markTaskComplete = goog.bind(function() { - this.history_.push(/** @type {!webdriver.promise.Task_} */ (task)); - activeFrame.setPendingTask(null); - }, this); - - this.trimHistory_(); - var self = this; - this.runInNewFrame_(task.execute, function(result) { - markTaskComplete(); - task.fulfill(result); - }, function(error) { - markTaskComplete(); - - if (!webdriver.promise.isError_(error) && - !webdriver.promise.isPromise(error)) { - error = Error(error); + function printTask(task, prefix) { + var ret = prefix + task; + if (opt_includeStackTraces && task.promise.stack_) { + ret += prefix + childIndent + + (task.promise.stack_.stack || task.promise.stack_) + .replace(/\n/g, prefix); + } + return ret; } + }, - task.reject(self.annotateError(/** @type {!Error} */ (error))); - }, true); -}; - + /** + * Returns the currently actively task queue for this flow. If there is no + * active queue, one will be created. + * @return {!TaskQueue} the currently active task queue for this flow. + * @private + */ + getActiveQueue_: function() { + if (this.activeQueue_) { + return this.activeQueue_; + } -/** - * @return {webdriver.promise.Task_} The next task to execute, or - * {@code null} if a frame was resolved. - * @private - */ -webdriver.promise.ControlFlow.prototype.getNextTask_ = function() { - var firstChild = this.activeFrame_.getFirstChild(); - if (!firstChild) { - if (!this.activeFrame_.isWaiting) { - this.resolveFrame_(this.activeFrame_); + this.activeQueue_ = new TaskQueue(this); + if (!this.taskQueues_) { + this.taskQueues_ = new Set(); } - return null; - } + this.taskQueues_.add(this.activeQueue_); + this.activeQueue_ + .once('end', this.onQueueEnd_, this) + .once('error', this.onQueueError_, this); - if (firstChild instanceof webdriver.promise.Frame_) { - this.activeFrame_ = firstChild; - return this.getNextTask_(); - } + asyncRun(() => this.activeQueue_ = null, this); + this.activeQueue_.start(); + return this.activeQueue_; + }, - firstChild.getParent().removeChild(firstChild); - return firstChild; -}; + /** + * 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. If + * the task function is a generator, the task will be executed using + * {@link webdriver.promise.consume}. + * + * @param {function(): (T|promise.Promise)} fn The function to + * call to start the task. If the function returns a + * {@link webdriver.promise.Promise}, this instance will wait for it to be + * resolved before starting the next task. + * @param {string=} opt_description A description of the task. + * @return {!promise.Promise} A promise that will be resolved + * with the result of the action. + * @template T + */ + execute: function(fn, opt_description) { + if (promise.isGenerator(fn)) { + fn = goog.partial(promise.consume, fn); + } + if (!this.hold_) { + var holdIntervalMs = 2147483647; // 2^31-1; max timer length for Node.js + this.hold_ = setInterval(goog.nullFunction, holdIntervalMs); + } -/** - * @param {!webdriver.promise.Frame_} frame The frame to resolve. - * @private - */ -webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) { - if (this.activeFrame_ === frame) { - // Frame parent is always another frame, but the compiler is not smart - // enough to recognize this. - this.activeFrame_ = - /** @type {webdriver.promise.Frame_} */ (frame.getParent()); - } + var task = new Task( + this, fn, opt_description || '', + {name: 'Task', top: promise.ControlFlow.prototype.execute}); - if (frame.getParent()) { - frame.getParent().removeChild(frame); - } - this.trimHistory_(); - frame.fulfill(); + var q = this.getActiveQueue_(); + q.enqueue(task); + this.emit(promise.ControlFlow.EventType.SCHEDULE_TASK, task.description); + return task.promise; + }, - if (!this.activeFrame_) { - this.commenceShutdown_(); - } -}; + /** + * Inserts a {@code setTimeout} into the command queue. This is equivalent to + * a thread sleep in a synchronous programming language. + * + * @param {number} ms The timeout delay, in milliseconds. + * @param {string=} opt_description A description to accompany the timeout. + * @return {!promise.Promise} A promise that will be resolved with + * the result of the action. + */ + timeout: function(ms, opt_description) { + return this.execute(function() { + return promise.delayed(ms); + }, opt_description); + }, + /** + * 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. + * + * If the condition is defined as a promise, the flow will wait for it to + * settle. If the timeout expires before the promise settles, the promise + * returned by this function will be rejected. + * + * If this function is invoked with `timeout === 0`, or the timeout is + * omitted, the flow will wait indefinitely for the condition to be satisfied. + * + * @param {(!promise.Promise|function())} condition The condition to poll, + * or a promise to wait on. + * @param {number=} opt_timeout How long to wait, in milliseconds, for the + * condition to hold before timing out. If omitted, the flow will wait + * indefinitely. + * @param {string=} opt_message An optional error message to include if the + * wait times out; defaults to the empty string. + * @return {!promise.Promise} A promise that will be fulfilled + * when the condition has been satisified. The promise shall be rejected + * if the wait times out waiting for the condition. + * @throws {TypeError} If condition is not a function or promise or if timeout + * is not a number >= 0. + * @template T + */ + wait: function(condition, opt_timeout, opt_message) { + var timeout = opt_timeout || 0; + if (!goog.isNumber(timeout) || timeout < 0) { + throw TypeError('timeout must be a number >= 0: ' + timeout); + } -/** - * 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. - * @param {*} error The reason the frame is being aborted; typically either - * an Error or string. - * @private - */ -webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) { - // Annotate the error value if it is Error-like. - if (webdriver.promise.isError_(error)) { - this.annotateError(/** @type {!Error} */ (error)); - } - this.numAbortedFrames_++; + if (promise.isPromise(condition)) { + return this.execute(function() { + if (!timeout) { + return condition; + } + return new promise.Promise(function(fulfill, reject) { + var start = goog.now(); + var timer = setTimeout(function() { + timer = null; + reject(Error((opt_message ? opt_message + '\n' : '') + + 'Timed out waiting for promise to resolve after ' + + (goog.now() - start) + 'ms')); + }, timeout); + + /** @type {Thenable} */(condition).then( + function(value) { + timer && clearTimeout(timer); + fulfill(value); + }, + function(error) { + timer && clearTimeout(timer); + reject(error); + }); + }); + }, opt_message || ''); + } - if (!this.activeFrame_) { - this.abortNow_(error); - return; - } + if (!goog.isFunction(condition)) { + throw TypeError('Invalid condition; must be a function or promise: ' + + goog.typeOf(condition)); + } - // Frame parent is always another frame, but the compiler is not smart - // enough to recognize this. - var parent = /** @type {webdriver.promise.Frame_} */ ( - this.activeFrame_.getParent()); - if (parent) { - parent.removeChild(this.activeFrame_); - } + if (promise.isGenerator(condition)) { + condition = goog.partial(promise.consume, condition); + } - var frame = this.activeFrame_; - this.activeFrame_ = parent; - frame.reject(error); -}; + var self = this; + return this.execute(function() { + var startTime = goog.now(); + return new promise.Promise(function(fulfill, reject) { + pollCondition(); + + function pollCondition() { + var conditionFn = /** @type {function()} */(condition); + self.execute(conditionFn).then(function(value) { + var elapsed = goog.now() - startTime; + if (!!value) { + fulfill(value); + } else if (timeout && elapsed >= timeout) { + reject(new Error((opt_message ? opt_message + '\n' : '') + + 'Wait timed out after ' + elapsed + 'ms')); + } else { + // Do not use asyncRun here because we need a non-micro yield + // here so the UI thread is given a chance when running in a + // browser. + setTimeout(pollCondition, 0); + } + }, reject); + } + }); + }, opt_message || ''); + }, + /** + * Executes a function in the next available turn of the JavaScript event + * loop. This ensures the function runs with its own task queue and any + * scheduled tasks will run in "parallel" to those scheduled in the current + * function. + * + * flow.execute(() => console.log('a')); + * flow.execute(() => console.log('b')); + * flow.execute(() => console.log('c')); + * flow.async(() => { + * flow.execute(() => console.log('d')); + * flow.execute(() => console.log('e')); + * }); + * flow.async(() => { + * flow.execute(() => console.log('f')); + * flow.execute(() => console.log('g')); + * }); + * flow.once('idle', () => console.log('fin')); + * // a + * // d + * // f + * // b + * // e + * // g + * // c + * // fin + * + * If the function itself throws, the error will be treated the same as an + * unhandled rejection within the control flow. + * + * __NOTE__: This function is considered _unstable_. + * + * @param {!Function} fn The function to execute. + * @param {Object=} opt_self The object in whose context to run the function. + * @param {...*} var_args Any arguments to pass to the function. + */ + async: function(fn, opt_self, var_args) { + asyncRun(function() { + // Clear any lingering queues, forces getActiveQueue_ to create a new one. + this.activeQueue_ = null; + var q = this.getActiveQueue_(); + try { + q.execute_(fn.bind(opt_self, var_args)); + } catch (ex) { + var cancellationError = promise.CancellationError.wrap(ex, + 'Function passed to ControlFlow.async() threw'); + cancellationError.silent_ = true; + q.abort_(cancellationError); + } finally { + this.activeQueue_ = null; + } + }, this); + }, -/** - * 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. - * - * @param {!Function} fn The function to execute. - * @param {function(*)} callback The function to call with a successful result. - * @param {function(*)} errback The function to call if there is an error. - * @param {boolean=} opt_activate Whether the active frame should be updated to - * the newly created frame so tasks are treated as sub-tasks. - * @private - */ -webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function( - fn, callback, errback, opt_activate) { - var newFrame = new webdriver.promise.Frame_(this), - self = this, - oldFrame = this.activeFrame_; - - try { - if (!this.activeFrame_) { - this.activeFrame_ = newFrame; - } else { - this.activeFrame_.addChild(newFrame); + /** + * Event handler for when a task queue is exhausted. This starts the shutdown + * sequence for this instance if there are no remaining task queues: after + * one turn of the event loop, this object will emit the + * {@link webdriver.promise.ControlFlow.EventType.IDLE IDLE} event to signal + * listeners that it has completed. During this wait, if another task is + * scheduled, the shutdown will be aborted. + * + * @param {!TaskQueue} q the completed task queue. + * @private + */ + onQueueEnd_: function(q) { + if (!this.taskQueues_) { + return; } + this.taskQueues_.delete(q); - // Activate the new frame to force tasks to be treated as sub-tasks of - // the parent frame. - if (opt_activate) { - this.activeFrame_ = newFrame; - } + vlog(1, () => q + ' has finished'); + vlog(1, () => this.taskQueues_.size + ' queues remain\n' + this, this); - try { - this.schedulingFrame_ = newFrame; - webdriver.promise.pushFlow_(this); - var result = fn(); - } finally { - webdriver.promise.popFlow_(); - this.schedulingFrame_ = null; + if (!this.taskQueues_.size) { + asserts.assert(!this.shutdownTask_, 'Already have a shutdown task??'); + this.shutdownTask_ = new MicroTask(this.shutdown_, this); } - newFrame.lockFrame(); + }, - // If there was nothing scheduled in the new frame we can discard the - // frame and return immediately. - if (!newFrame.children_.length) { - removeNewFrame(); - webdriver.promise.asap(result, callback, errback); - return; + /** + * Event handler for when a task queue terminates with an error. This triggers + * the cancellation of all other task queues and a + * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event. + * If there are no error event listeners registered with this instance, the + * error will be rethrown to the global error handler. + * + * @param {*} error the error that caused the task queue to terminate. + * @param {!TaskQueue} q the task queue. + * @private + */ + onQueueError_: function(error, q) { + if (this.taskQueues_) { + this.taskQueues_.delete(q); } + this.cancelQueues_(promise.CancellationError.wrap( + error, 'There was an uncaught error in the control flow')); + this.cancelShutdown_(); + this.cancelHold_(); + + var listeners = this.listeners( + promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + if (!listeners.length) { + throwException(error); + } else { + this.reportUncaughtException_(error); + } + }, - newFrame.then(function() { - webdriver.promise.asap(result, callback, errback); - }, function(e) { - if (result instanceof webdriver.promise.Promise && result.isPending()) { - result.cancel(e); - e = result; + /** + * Cancels all remaining task queues. + * @param {!promise.CancellationError} reason The cancellation reason. + * @private + */ + cancelQueues_: function(reason) { + reason.silent_ = true; + if (this.taskQueues_) { + for (var q of this.taskQueues_) { + q.removeAllListeners(); + q.abort_(reason); } - errback(e); - }); - } catch (ex) { - removeNewFrame(new webdriver.promise.CanceledTaskError_(ex)); - errback(ex); - } + this.taskQueues_.clear(); + this.taskQueues_ = null; + } + }, /** - * @param {webdriver.promise.CanceledTaskError_=} opt_err If provided, the - * error that triggered the removal of this frame. + * Reports an uncaught exception using a + * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event. + * + * @param {*} e the error to report. + * @private */ - function removeNewFrame(opt_err) { - var parent = newFrame.getParent(); - if (parent) { - parent.removeChild(newFrame); + reportUncaughtException_: function(e) { + this.emit(promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, e); + }, + + /** @private */ + cancelHold_: function() { + if (this.hold_) { + clearInterval(this.hold_); + this.hold_ = null; } + }, + + /** @private */ + shutdown_: function() { + vlog(1, () => 'Going idle: ' + this); + this.cancelHold_(); + this.shutdownTask_ = null; + this.emit(promise.ControlFlow.EventType.IDLE); + }, - if (opt_err) { - newFrame.cancelRemainingTasks(opt_err); + /** + * Cancels the shutdown sequence if it is currently scheduled. + * @private + */ + cancelShutdown_: function() { + if (this.shutdownTask_) { + this.shutdownTask_.cancel(); + this.shutdownTask_ = null; } - self.activeFrame_ = oldFrame; } -}; +}); /** - * Commences the shutdown sequence for this instance. After one turn of the - * event loop, this object will emit the - * {@link 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. - * @private + * Events that may be emitted by an {@link webdriver.promise.ControlFlow}. + * @enum {string} */ -webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() { - if (!this.shutdownId_) { - // Go ahead and stop the event loop now. If we're in here, then there are - // no more frames with tasks to execute. If we waited to cancel the event - // loop in our timeout below, the event loop could trigger *before* the - // timeout, generating an error from there being no frames. - // If #execute is called before the timeout below fires, it will cancel - // the timeout and restart the event loop. - this.cancelEventLoop_(); +promise.ControlFlow.EventType = { - var self = this; - self.shutdownId_ = self.timer.setTimeout(function() { - self.shutdownId_ = null; - self.emit(webdriver.promise.ControlFlow.EventType.IDLE); - }, 0); - } -}; + /** Emitted when all tasks have been successfully executed. */ + IDLE: 'idle', + /** Emitted when a ControlFlow has been reset. */ + RESET: 'reset', -/** - * Cancels the shutdown sequence if it is currently scheduled. - * @private - */ -webdriver.promise.ControlFlow.prototype.cancelShutdown_ = function() { - if (this.shutdownId_) { - this.timer.clearTimeout(this.shutdownId_); - this.shutdownId_ = null; - } + /** Emitted whenever a new task has been scheduled. */ + SCHEDULE_TASK: 'scheduleTask', + + /** + * Emitted whenever a control flow aborts due to an unhandled promise + * rejection. This event will be emitted along with the offending rejection + * reason. Upon emitting this event, the control flow will empty its task + * queue and revert to its initial state. + */ + UNCAUGHT_EXCEPTION: 'uncaughtException' }; /** - * Aborts this flow, abandoning all remaining tasks. If there are - * listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the - * offending {@code error}, otherwise, the {@code error} will be rethrown to the - * global error handler. - * @param {*} error Object describing the error that caused the flow to - * abort; usually either an Error or string value. - * @private + * Wraps a function to execute as a cancellable micro task. + * @final */ -webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) { - this.activeFrame_ = null; - this.cancelShutdown_(); - this.cancelEventLoop_(); - - var listeners = this.listeners( - webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); - if (!listeners.length) { - this.timer.setTimeout(function() { - throw error; - }, 0); - } else { - this.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, - error); - } -}; +var MicroTask = goog.defineClass(null, { + /** + * @param {function(this: THIS)} fn The function to run as a micro task. + * @param {THIS=} opt_scope The scope to run the function in. + * @template THIS + */ + constructor: function(fn, opt_scope) { + /** @private {boolean} */ + this.cancelled_ = false; + asyncRun(function() { + if (!this.cancelled_) { + fn.call(opt_scope); + } + }, this); + }, + /** + * Cancels the execution of this task. Note: this will not prevent the task + * timer from firing, just the invocation of the wrapped function. + */ + cancel: function() { + this.cancelled_ = true; + } +}); /** - * A single node in an {@link webdriver.promise.ControlFlow}'s task tree. - * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs - * to. - * @constructor - * @extends {webdriver.promise.Deferred} - * @private + * A task to be executed by a {@link webdriver.promise.ControlFlow}. + * + * @final */ -webdriver.promise.Node_ = function(flow) { - webdriver.promise.Deferred.call(this, null, flow); -}; -goog.inherits(webdriver.promise.Node_, webdriver.promise.Deferred); +var Task = goog.defineClass(promise.Deferred, { + /** + * @param {!promise.ControlFlow} flow The flow this instances belongs + * to. + * @param {function(): (T|!promise.Promise)} fn The function to + * call when the task executes. If it returns a + * {@link webdriver.promise.Promise}, the flow will wait for it to be + * resolved before starting the next task. + * @param {string} description A description of the task for debugging. + * @param {{name: string, top: !Function}=} opt_stackOptions Options to use + * when capturing the stacktrace for when this task was created. + * @constructor + * @extends {promise.Deferred} + * @template T + */ + constructor: function(flow, fn, description, opt_stackOptions) { + Task.base(this, 'constructor', flow); + /** @type {function(): (T|!promise.Promise)} */ + this.execute = fn; -/** - * This node's parent. - * @private {webdriver.promise.Node_} - */ -webdriver.promise.Node_.prototype.parent_ = null; + /** @type {string} */ + this.description = description; + /** @type {TaskQueue} */ + this.queue = null; -/** @return {webdriver.promise.Node_} This node's parent. */ -webdriver.promise.Node_.prototype.getParent = function() { - return this.parent_; -}; + /** + * Whether this task is volatile. Volatile tasks may be registered in a + * a task queue, but will be dropped on the next turn of the JS event loop + * if still marked volatile. + * @type {boolean} + */ + this.isVolatile = false; + if (opt_stackOptions) { + this.promise.stack_ = promise.captureStackTrace( + opt_stackOptions.name, this.description, opt_stackOptions.top); + } + }, -/** - * @param {webdriver.promise.Node_} parent This node's new parent. - */ -webdriver.promise.Node_.prototype.setParent = function(parent) { - this.parent_ = parent; -}; + /** @override */ + toString: function() { + return 'Task::' + goog.getUid(this) + '<' + this.description + '>'; + } +}); -/** - * @return {!webdriver.promise.Node_} The root of this node's tree. - */ -webdriver.promise.Node_.prototype.getRoot = function() { - var root = this; - while (root.parent_) { - root = root.parent_; - } - return root; +/** @enum {string} */ +var TaskQueueState = { + NEW: 'new', + STARTED: 'started', + FINISHED: 'finished' }; - /** - * An execution frame within a {@link webdriver.promise.ControlFlow}. Each - * frame represents the execution context for either a - * {@link webdriver.promise.Task_} or a callback on a - * {@link webdriver.promise.Deferred}. - * - *

        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. - * - * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs - * to. - * @constructor - * @extends {webdriver.promise.Node_} - * @private + * @final */ -webdriver.promise.Frame_ = function(flow) { - webdriver.promise.Node_.call(this, flow); +var TaskQueue = goog.defineClass(EventEmitter, { + /** @param {!promise.ControlFlow} flow . */ + constructor: function(flow) { + TaskQueue.base(this, 'constructor'); + goog.getUid(this); - var reject = goog.bind(this.reject, this); - var cancelRemainingTasks = goog.bind(this.cancelRemainingTasks, this); + /** @private {string} */ + this.name_ = 'TaskQueue::' + goog.getUid(this); - /** @override */ - this.reject = function(e) { - cancelRemainingTasks(new webdriver.promise.CanceledTaskError_(e)); - reject(e); - }; + /** @private {!promise.ControlFlow} */ + this.flow_ = flow; - /** - * @private {!Array.} - */ - this.children_ = []; -}; -goog.inherits(webdriver.promise.Frame_, webdriver.promise.Node_); + /** @private {!Array} */ + this.tasks_ = []; + /** @private {Array} */ + this.volatileTasks_ = null; -/** - * The task currently being executed within this frame. - * @private {webdriver.promise.Task_} - */ -webdriver.promise.Frame_.prototype.pendingTask_ = null; + /** @private {Array} */ + this.interrupts_ = null; + /** @private {({task: !Task, q: !TaskQueue}|null)} */ + this.pending_ = null; -/** - * 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 {@link 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);
        - * 
        - * - * @private {boolean} - */ -webdriver.promise.Frame_.prototype.isActive_ = false; + /** @private {TaskQueueState} */ + this.state_ = TaskQueueState.NEW; + /** @private {!Set} */ + this.unhandledRejections_ = new Set(); + }, -/** - * 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 {@link #isActive_ active}, any new frames which are - * added represent callbacks on a {@link webdriver.promise.Deferred}, whose - * tasks must be given priority over previously scheduled tasks. - * - * @private {boolean} - */ -webdriver.promise.Frame_.prototype.isLocked_ = false; + /** @override */ + toString: function() { + return 'TaskQueue::' + goog.getUid(this); + }, + /** + * @param {!webdriver.promise.Promise} promise . + */ + addUnhandledRejection: function(promise) { + // TODO: node 4.0.0+ + vlog(2, () => this + ' registering unhandled rejection: ' + promise, this); + this.unhandledRejections_.add(promise); + }, -/** - * A reference to the last node inserted in this frame. - * @private {webdriver.promise.Node_} - */ -webdriver.promise.Frame_.prototype.lastInsertedChild_ = null; + /** + * @param {!webdriver.promise.Promise} promise . + */ + clearUnhandledRejection: function(promise) { + var deleted = this.unhandledRejections_.delete(promise); + if (deleted) { + // TODO: node 4.0.0+ + vlog(2, () => this + ' clearing unhandled rejection: ' + promise, this); + } + }, + /** + * Enqueues a new task for execution. + * @param {!Task} task The task to enqueue. + * @throws {Error} If this instance has already started execution. + */ + enqueue: function(task) { + if (this.state_ !== TaskQueueState.NEW) { + throw Error('TaskQueue has started: ' + this); + } -/** - * 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 - * - * @param {!webdriver.promise.CanceledTaskError_} error The cancellation - * error. - */ -webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(error) { - goog.array.forEach(this.children_, function(child) { - if (child instanceof webdriver.promise.Frame_) { - child.cancelRemainingTasks(error); - } else { - // None of the previously registered listeners should be notified that - // the task is being canceled, however, we need at least one errback - // to prevent the cancellation from bubbling up. - child.removeAll(); - child.thenCatch(goog.nullFunction); - child.cancel(error); + if (task.queue) { + throw Error('Task is already scheduled in another queue'); } - }); -}; + if (task.isVolatile) { + this.volatileTasks_ = this.volatileTasks_ || []; + this.volatileTasks_.push(task); + } -/** - * @return {webdriver.promise.Task_} The task currently executing - * within this frame, if any. - */ -webdriver.promise.Frame_.prototype.getPendingTask = function() { - return this.pendingTask_; -}; + this.tasks_.push(task); + task.queue = this; + task.promise[CANCEL_HANDLER_SYMBOL] = + this.onTaskCancelled_.bind(this, task); + vlog(1, () => this + '.enqueue(' + task + ')', this); + vlog(2, () => this.flow_.toString(), this); + }, -/** - * @param {webdriver.promise.Task_} task The task currently - * executing within this frame, if any. - */ -webdriver.promise.Frame_.prototype.setPendingTask = function(task) { - this.pendingTask_ = task; -}; + /** + * Schedules the callbacks registered on the given promise in this queue. + * + * @param {!promise.Promise} promise the promise whose callbacks should be + * registered as interrupts in this task queue. + * @throws {Error} if this queue has already finished. + */ + scheduleCallbacks: function(promise) { + if (this.state_ === TaskQueueState.FINISHED) { + throw new Error('cannot interrupt a finished q(' + this + ')'); + } + + if (this.pending_ && this.pending_.task.promise === promise) { + this.pending_.task.promise.queue_ = null; + this.pending_ = null; + asyncRun(this.executeNext_, this); + } + if (!promise.callbacks_) { + return; + } + promise.callbacks_.forEach(function(cb) { + cb.promise[CANCEL_HANDLER_SYMBOL] = + this.onTaskCancelled_.bind(this, cb); -/** Locks this frame. */ -webdriver.promise.Frame_.prototype.lockFrame = function() { - this.isLocked_ = true; -}; + cb.isVolatile = false; + if (cb.queue === this && this.tasks_.indexOf(cb) !== -1) { + return; + } + if (cb.queue) { + cb.queue.dropTask_(cb); + } -/** - * Adds a new node to this frame. - * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node - * The node to insert. - */ -webdriver.promise.Frame_.prototype.addChild = function(node) { - if (this.lastInsertedChild_ && - this.lastInsertedChild_ instanceof webdriver.promise.Frame_ && - !this.lastInsertedChild_.isLocked_) { - this.lastInsertedChild_.addChild(node); - return; - } + cb.queue = this; + if (!this.interrupts_) { + this.interrupts_ = []; + } + this.interrupts_.push(cb); + }, this); + promise.callbacks_ = null; + vlog(2, () => this + ' interrupted\n' + this.flow_, this); + }, + + /** + * Starts executing tasks in this queue. Once called, no further tasks may + * be {@linkplain #enqueue() enqueued} with this instance. + * + * @throws {Error} if this queue has already been started. + */ + start: function() { + if (this.state_ !== TaskQueueState.NEW) { + throw new Error('TaskQueue has already started'); + } + // Always asynchronously execute next, even if there doesn't look like + // there is anything in the queue. This will catch pending unhandled + // rejections that were registered before start was called. + asyncRun(this.executeNext_, this); + }, - node.setParent(this); + /** + * Aborts this task queue. If there are any scheduled tasks, they are silently + * cancelled and discarded (their callbacks will never fire). If this queue + * has a _pending_ task, the abortion error is used to cancel that task. + * Otherwise, this queue will emit an error event. + * + * @param {*} error The abortion reason. + * @private + */ + abort_: function(error) { + var cancellation; - if (this.isActive_ && node instanceof webdriver.promise.Frame_) { - var index = 0; - if (this.lastInsertedChild_ instanceof - webdriver.promise.Frame_) { - index = goog.array.indexOf(this.children_, this.lastInsertedChild_) + 1; + if (error instanceof FlowResetError) { + cancellation = error; + } else { + cancellation = new DiscardedTaskError(error); } - goog.array.insertAt(this.children_, node, index); - this.lastInsertedChild_ = node; - return; - } - this.lastInsertedChild_ = node; - this.children_.push(node); -}; + if (this.interrupts_ && this.interrupts_.length) { + this.interrupts_.forEach((t) => t.reject(cancellation)); + this.interrupts_ = []; + } + if (this.tasks_ && this.tasks_.length) { + this.tasks_.forEach((t) => t.reject(cancellation)); + this.tasks_ = []; + } -/** - * @return {(webdriver.promise.Frame_|webdriver.promise.Task_)} This frame's - * fist child. - */ -webdriver.promise.Frame_.prototype.getFirstChild = function() { - this.isActive_ = true; - this.lastInsertedChild_ = null; - return this.children_[0]; -}; + if (this.pending_) { + vlog(2, () => this + '.abort(); cancelling pending task', this); + this.pending_.task.cancel( + /** @type {!webdriver.promise.CancellationError} */(error)); + } else { + vlog(2, () => this + '.abort(); emitting error event', this); + this.emit('error', error, this); + } + }, -/** - * Removes a child from this frame. - * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} child - * The child to remove. - */ -webdriver.promise.Frame_.prototype.removeChild = function(child) { - var index = goog.array.indexOf(this.children_, child); - child.setParent(null); - goog.array.removeAt(this.children_, index); - if (this.lastInsertedChild_ === child) { - this.lastInsertedChild_ = null; - } -}; + /** @private */ + executeNext_: function() { + if (this.state_ === TaskQueueState.FINISHED) { + return; + } + this.state_ = TaskQueueState.STARTED; + this.dropVolatileTasks_(); + if (this.pending_ != null || this.processUnhandledRejections_()) { + return; + } -/** @override */ -webdriver.promise.Frame_.prototype.toString = function() { - return '[' + goog.array.map(this.children_, function(child) { - return child.toString(); - }).join(', ') + ']'; -}; + var task; + do { + task = this.getNextTask_(); + } while (task && !task.promise.isPending()); + + if (!task) { + this.state_ = TaskQueueState.FINISHED; + this.tasks_ = []; + this.interrupts_ = null; + vlog(2, () => this + '.emit(end)', this); + this.emit('end', this); + return; + } + var self = this; + var subQ = new TaskQueue(this.flow_); + subQ.once('end', () => self.onTaskComplete_(result)) + .once('error', (e) => self.onTaskFailure_(result, e)); + vlog(2, () => self + ' created ' + subQ + ' for ' + task); + var result = undefined; + try { + this.pending_ = {task: task, q: subQ}; + task.promise.queue_ = this; + result = subQ.execute_(task.execute); + subQ.start(); + } catch (ex) { + subQ.abort_(ex); + } + }, -/** - * A task to be executed by a {@link webdriver.promise.ControlFlow}. - * - * @param {!webdriver.promise.ControlFlow} flow The flow this instances belongs - * to. - * @param {!Function} fn The function to call when the task executes. If it - * returns a {@code webdriver.promise.Promise}, the flow will wait - * for it to be resolved before starting the next task. - * @param {string} description A description of the task for debugging. - * @param {!webdriver.stacktrace.Snapshot} snapshot A snapshot of the stack - * when this task was scheduled. - * @constructor - * @extends {webdriver.promise.Node_} - * @private - */ -webdriver.promise.Task_ = function(flow, fn, description, snapshot) { - webdriver.promise.Node_.call(this, flow); /** - * Executes this task. - * @type {!Function} + * @param {!Function} fn . + * @return {T} . + * @template T + * @private */ - this.execute = fn; + execute_: function(fn) { + try { + activeFlows.push(this.flow_); + this.flow_.activeQueue_ = this; + return fn(); + } finally { + this.flow_.activeQueue_ = null; + this.dropVolatileTasks_(); + activeFlows.pop(); + } + }, - /** @private {string} */ - this.description_ = description; + /** + * Process any unhandled rejections registered with this task queue. If there + * is a rejection, this queue will be aborted with the rejection error. If + * there are multiple rejections registered, this queue will be aborted with + * a {@link promise.MultipleUnhandledRejectionError}. + * @return {boolean} whether there was an unhandled rejection. + * @private + */ + processUnhandledRejections_: function() { + if (!this.unhandledRejections_.size) { + return false; + } - /** @private {!webdriver.stacktrace.Snapshot} */ - this.snapshot_ = snapshot; -}; -goog.inherits(webdriver.promise.Task_, webdriver.promise.Node_); + var errors = new Set(); + for (var rejection of this.unhandledRejections_) { + errors.add(rejection.value_); + } + this.unhandledRejections_.clear(); + var errorToReport = errors.size === 1 + ? errors.values().next().value + : new promise.MultipleUnhandledRejectionError(errors); -/** @return {string} This task's description. */ -webdriver.promise.Task_.prototype.getDescription = function() { - return this.description_; -}; + vlog(1, () => this + ' aborting due to unhandled rejections', this); + if (this.flow_.propagateUnhandledRejections_) { + this.abort_(errorToReport); + return true; + } else { + vlog(1, 'error propagation disabled; reporting to control flow'); + this.flow_.reportUncaughtException_(errorToReport); + return false; + } + }, + /** + * Drops any volatile tasks scheduled within this task queue. + * @private + */ + dropVolatileTasks_: function() { + if (!this.volatileTasks_) { + return; + } + for (var task of this.volatileTasks_) { + if (task.isVolatile) { + vlog(2, () => this + ' dropping volatile task ' + task, this); + this.dropTask_(task); + } + } + this.volatileTasks_ = null; + }, -/** @override */ -webdriver.promise.Task_.prototype.toString = function() { - var stack = this.snapshot_.getStacktrace(); - var ret = this.description_; - if (stack.length) { - if (this.description_) { - ret += '\n'; + /** + * @param {!Task} task The task to drop. + * @private + */ + dropTask_: function(task) { + var index; + if (this.interrupts_) { + index = this.interrupts_.indexOf(task); + if (index != -1) { + task.queue = null; + this.interrupts_.splice(index, 1); + return; + } } - ret += stack.join('\n'); - } - return ret; -}; + index = this.tasks_.indexOf(task); + if (index != -1) { + task.queue = null; + this.tasks_.splice(index, 1); + } + }, + /** + * @param {!Task} task The task that was cancelled. + * @param {!promise.CancellationError} reason The cancellation reason. + * @private + */ + onTaskCancelled_: function(task, reason) { + if (this.pending_ && this.pending_.task === task) { + this.pending_.q.abort_(reason); + } else { + this.dropTask_(task); + } + }, -/** - * Special error used to signal when a task is canceled because a previous - * task in the same frame failed. - * @param {*} err The error that caused the task cancellation. - * @constructor - * @extends {goog.debug.Error} - * @private - */ -webdriver.promise.CanceledTaskError_ = function(err) { - goog.base(this, 'Task discarded due to a previous task failure: ' + err); -}; -goog.inherits(webdriver.promise.CanceledTaskError_, goog.debug.Error); + /** + * @param {*} value the value originally returned by the task function. + * @private + */ + onTaskComplete_: function(value) { + if (this.pending_) { + this.pending_.task.fulfill(value); + } + }, + /** + * @param {*} taskFnResult the value originally returned by the task function. + * @param {*} error the error that caused the task function to terminate. + * @private + */ + onTaskFailure_: function(taskFnResult, error) { + if (promise.Thenable.isImplementation(taskFnResult)) { + taskFnResult.cancel(promise.CancellationError.wrap(error)); + } + this.pending_.task.reject(error); + }, -/** @override */ -webdriver.promise.CanceledTaskError_.prototype.name = 'CanceledTaskError'; + /** + * @return {(Task|undefined)} the next task scheduled within this queue, + * if any. + * @private + */ + getNextTask_: function() { + var task = undefined; + if (this.interrupts_) { + task = this.interrupts_.shift(); + } + if (!task && this.tasks_) { + task = this.tasks_.shift(); + } + return task; + } +}); /** * The default flow to use if no others are active. - * @private {!webdriver.promise.ControlFlow} + * @type {!promise.ControlFlow} */ -webdriver.promise.defaultFlow_ = new webdriver.promise.ControlFlow(); +var defaultFlow = new promise.ControlFlow(); /** @@ -2055,46 +2906,30 @@ webdriver.promise.defaultFlow_ = new webdriver.promise.ControlFlow(); * 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. - * @private {!Array.} + * @type {!Array} */ -webdriver.promise.activeFlows_ = []; +var activeFlows = []; /** * Changes the default flow to use when no others are active. - * @param {!webdriver.promise.ControlFlow} flow The new default flow. + * @param {!promise.ControlFlow} flow The new default flow. * @throws {Error} If the default flow is not currently active. */ -webdriver.promise.setDefaultFlow = function(flow) { - if (webdriver.promise.activeFlows_.length) { +promise.setDefaultFlow = function(flow) { + if (activeFlows.length) { throw Error('You may only change the default flow while it is active'); } - webdriver.promise.defaultFlow_ = flow; -}; - - -/** - * @return {!webdriver.promise.ControlFlow} The currently active control flow. - */ -webdriver.promise.controlFlow = function() { - return /** @type {!webdriver.promise.ControlFlow} */ ( - goog.array.peek(webdriver.promise.activeFlows_) || - webdriver.promise.defaultFlow_); + defaultFlow = flow; }; /** - * @param {!webdriver.promise.ControlFlow} flow The new flow. - * @private + * @return {!promise.ControlFlow} The currently active control flow. */ -webdriver.promise.pushFlow_ = function(flow) { - webdriver.promise.activeFlows_.push(flow); -}; - - -/** @private */ -webdriver.promise.popFlow_ = function() { - webdriver.promise.activeFlows_.pop(); +promise.controlFlow = function() { + return /** @type {!promise.ControlFlow} */ ( + Arrays.peek(activeFlows) || defaultFlow); }; @@ -2102,15 +2937,107 @@ webdriver.promise.popFlow_ = function() { * 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. - * @param {function(!webdriver.promise.ControlFlow)} callback The entry point + * @param {function(!promise.ControlFlow)} callback The entry point * to the newly created flow. - * @return {!webdriver.promise.Promise} A promise that resolves to the callback + * @return {!promise.Promise} A promise that resolves to the callback * result. */ -webdriver.promise.createFlow = function(callback) { - var flow = new webdriver.promise.ControlFlow( - webdriver.promise.defaultFlow_.timer); +promise.createFlow = function(callback) { + var flow = new promise.ControlFlow; return flow.execute(function() { return callback(flow); }); }; + + +/** + * Tests is a function is a generator. + * @param {!Function} fn The function to test. + * @return {boolean} Whether the function is a generator. + */ +promise.isGenerator = function(fn) { + return fn.constructor.name === 'GeneratorFunction'; +}; + + +/** + * Consumes a {@code GeneratorFunction}. Each time the generator yields a + * promise, this function will wait for it to be fulfilled before feeding the + * fulfilled value back into {@code next}. Likewise, if a yielded promise is + * rejected, the rejection error will be passed to {@code throw}. + * + * __Example 1:__ the Fibonacci Sequence. + * + * promise.consume(function* fibonacci() { + * var n1 = 1, n2 = 1; + * for (var i = 0; i < 4; ++i) { + * var tmp = yield n1 + n2; + * n1 = n2; + * n2 = tmp; + * } + * return n1 + n2; + * }).then(function(result) { + * console.log(result); // 13 + * }); + * + * __Example 2:__ a generator that throws. + * + * promise.consume(function* () { + * yield promise.delayed(250).then(function() { + * throw Error('boom'); + * }); + * }).thenCatch(function(e) { + * console.log(e.toString()); // Error: boom + * }); + * + * @param {!Function} generatorFn The generator function to execute. + * @param {Object=} opt_self The object to use as "this" when invoking the + * initial generator. + * @param {...*} var_args Any arguments to pass to the initial generator. + * @return {!promise.Promise} A promise that will resolve to the + * generator's final result. + * @throws {TypeError} If the given function is not a generator. + */ +promise.consume = function(generatorFn, opt_self, var_args) { + if (!promise.isGenerator(generatorFn)) { + throw new TypeError('Input is not a GeneratorFunction: ' + + generatorFn.constructor.name); + } + + var deferred = promise.defer(); + var generator = generatorFn.apply(opt_self, Arrays.slice(arguments, 2)); + callNext(); + return deferred.promise; + + /** @param {*=} opt_value . */ + function callNext(opt_value) { + pump(generator.next, opt_value); + } + + /** @param {*=} opt_error . */ + function callThrow(opt_error) { + // Dictionary lookup required because Closure compiler's built-in + // externs does not include GeneratorFunction.prototype.throw. + pump(generator['throw'], opt_error); + } + + function pump(fn, opt_arg) { + if (!deferred.isPending()) { + return; // Defererd was cancelled; silently abort. + } + + try { + var result = fn.call(generator, opt_arg); + } catch (ex) { + deferred.reject(ex); + return; + } + + if (result.done) { + deferred.fulfill(result.value); + return; + } + + promise.asap(result.value, callNext, callThrow); + } +}; diff --git a/lib/webdriver/serializable.js b/lib/webdriver/serializable.js new file mode 100644 index 0000000..16db027 --- /dev/null +++ b/lib/webdriver/serializable.js @@ -0,0 +1,41 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.provide('webdriver.Serializable'); + + + +/** + * Defines an object that can be asynchronously serialized to its WebDriver + * wire representation. + * + * @constructor + * @template T + */ +webdriver.Serializable = function() {}; + + +/** + * Returns either this instance's serialized represention, if immediately + * available, or a promise for its serialized representation. This function is + * conceptually equivalent to objects that have a {@code toJSON()} property, + * except the serialize() result may be a promise or an object containing a + * promise (which are not directly JSON friendly). + * + * @return {!(T|IThenable.)} This instance's serialized wire format. + */ +webdriver.Serializable.prototype.serialize = goog.abstractMethod; diff --git a/lib/webdriver/session.js b/lib/webdriver/session.js index 03d3219..1550226 100644 --- a/lib/webdriver/session.js +++ b/lib/webdriver/session.js @@ -1,16 +1,19 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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. goog.provide('webdriver.Session'); diff --git a/lib/webdriver/stacktrace.js b/lib/webdriver/stacktrace.js index a0cec8f..3b34435 100644 --- a/lib/webdriver/stacktrace.js +++ b/lib/webdriver/stacktrace.js @@ -1,18 +1,19 @@ -// Copyright 2009 The Closure Library Authors. All Rights Reserved. -// Copyright 2012 Selenium comitters -// Copyright 2012 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Tools for parsing and pretty printing error stack traces. This @@ -60,11 +61,10 @@ webdriver.stacktrace.Snapshot = function(opt_slice) { } /** - * The error's stacktrace. This must be accessed immediately to ensure Opera - * computes the context correctly. + * The error's stacktrace. * @private {string} */ - this.stack_ = webdriver.stacktrace.getStack_(error); + this.stack_ = webdriver.stacktrace.getStack(error); }; @@ -353,7 +353,9 @@ webdriver.stacktrace.V8_LOCATION_PATTERN_ = ' (?:\\((.*)\\)|(.*))'; * @private {!RegExp} * @const */ -webdriver.stacktrace.V8_STACK_FRAME_REGEXP_ = new RegExp('^ at' + +webdriver.stacktrace.V8_STACK_FRAME_REGEXP_ = new RegExp('^\\s+at' + + // Prevent intersections with IE10 stack frame regex. + '(?! (?:Anonymous function|Global code|eval code) )' + '(?:' + webdriver.stacktrace.V8_FUNCTION_CALL_PATTERN_ + ')?' + webdriver.stacktrace.V8_LOCATION_PATTERN_ + '$'); @@ -390,49 +392,18 @@ webdriver.stacktrace.FIREFOX_STACK_FRAME_REGEXP_ = new RegExp('^' + '(?::0|' + webdriver.stacktrace.URL_PATTERN_ + ')$'); -/** - * RegExp pattern for an anonymous function call in an Opera stack frame. - * Creates 2 (optional) submatches: the context object and function name. - * @private {string} - * @const - */ -webdriver.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ = - ''; - - -/** - * 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). - * @private {string} - * @const - */ -webdriver.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ = - '(?:(?:(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')|' + - webdriver.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ + - ')(?:\\(.*\\)))?@'; - - -/** - * Regular expression for parsing on stack frame in Opera 11.68+ - * @private {!RegExp} - * @const - */ -webdriver.stacktrace.OPERA_STACK_FRAME_REGEXP_ = new RegExp('^' + - webdriver.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ + - webdriver.stacktrace.URL_PATTERN_ + '?$'); - - /** * RegExp pattern for function call in a Chakra (IE) stack trace. This - * expression allows for identifiers like 'Anonymous function', 'eval code', - * and 'Global code'. + * expression creates 2 submatches on the (optional) context and function name, + * matching identifiers like 'foo.Bar.prototype.baz', 'Anonymous function', + * 'eval code', and 'Global code'. * @private {string} * @const */ -webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ = '(' + - webdriver.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)'; +webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ = + '(?:(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + + '(?:\\.' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')*)\\.)?' + + '(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)'; /** @@ -511,14 +482,9 @@ webdriver.stacktrace.parseStackFrame_ = function(frameStr) { return new webdriver.stacktrace.Frame('', m[1], '', m[2]); } - m = frameStr.match(webdriver.stacktrace.OPERA_STACK_FRAME_REGEXP_); - if (m) { - return new webdriver.stacktrace.Frame(m[2], m[1] || m[3], '', m[4]); - } - m = frameStr.match(webdriver.stacktrace.CHAKRA_STACK_FRAME_REGEXP_); if (m) { - return new webdriver.stacktrace.Frame('', m[1], '', m[2]); + return new webdriver.stacktrace.Frame(m[1], m[2], '', m[3]); } if (frameStr == webdriver.stacktrace.UNKNOWN_CLOSURE_FRAME_ || @@ -562,11 +528,13 @@ webdriver.stacktrace.parseLongFirefoxFrame_ = function(frameStr) { * 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. - * @param {!(Error|goog.testing.JsUnitException)} error The error. + * @param {(Error|goog.testing.JsUnitException)} error The error. * @return {string} The stack trace string. - * @private */ -webdriver.stacktrace.getStack_ = function(error) { +webdriver.stacktrace.getStack = function(error) { + if (!error) { + return ''; + } var stack = error.stack || error.stackTrace || ''; var errorStr = error + '\n'; if (goog.string.startsWith(stack, errorStr)) { @@ -582,9 +550,21 @@ webdriver.stacktrace.getStack_ = function(error) { * @return {!(Error|goog.testing.JsUnitException)} The formatted error. */ webdriver.stacktrace.format = function(error) { - var stack = webdriver.stacktrace.getStack_(error); + var stack = webdriver.stacktrace.getStack(error); var frames = webdriver.stacktrace.parse_(stack); + // If the original stack is in an unexpected format, our formatted stack + // trace will be a bunch of " at " lines. If this is the case, + // just return the error unmodified to avoid losing information. This is + // necessary since the user may have defined a custom stack formatter in + // V8 via Error.prepareStackTrace. See issue 7994. + var isAnonymousFrame = function(frame) { + return frame.toString() === ' at '; + }; + if (frames.length && goog.array.every(frames, isAnonymousFrame)) { + return error; + } + // Older versions of IE simply return [object Error] for toString(), so // only use that as a last resort. var errorStr = ''; @@ -622,12 +602,7 @@ webdriver.stacktrace.parse_ = function(stack) { // The first two frames will be: // webdriver.stacktrace.Snapshot() // webdriver.stacktrace.get() - // In the case of Opera, sometimes an extra frame is injected in the next - // frame with a reported line number of zero. The next line detects that - // case and skips that frame. - if (!(goog.userAgent.OPERA && i == 2 && frame.getLine() == 0)) { - frames.push(frame || webdriver.stacktrace.ANONYMOUS_FRAME_); - } + frames.push(frame || webdriver.stacktrace.ANONYMOUS_FRAME_); } return frames; }; diff --git a/lib/webdriver/test/builder_test.js b/lib/webdriver/test/builder_test.js new file mode 100644 index 0000000..be88026 --- /dev/null +++ b/lib/webdriver/test/builder_test.js @@ -0,0 +1,53 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.jsunit'); +goog.require('webdriver.Builder'); + + + +function testInitializeSessionIdFromQueryString_notSet() { + var builder = new webdriver.Builder({ + location: '/somelocation' + }); + assertUndefined(builder.getSession()); +} + + +function testInitializeSessionIdfromQueryString_set() { + var builder = new webdriver.Builder({ + location: '/somelocation?wdsid=foo' + }); + assertEquals('foo', builder.getSession()); +} + + +function testInitializeServerUrlFromQueryString_notSet() { + var builder = new webdriver.Builder({ + location: '/somelocation' + }); + assertEquals(webdriver.Builder.DEFAULT_SERVER_URL, + builder.getServerUrl()); +} + + +function testInitializeServerUrlFromQueryString_set() { + var builder = new webdriver.Builder({ + location: '/somelocation?wdurl=http://www.example.com' + }); + assertEquals('http://www.example.com', builder.getServerUrl()); +} diff --git a/lib/webdriver/test/capabilities_test.js b/lib/webdriver/test/capabilities_test.js new file mode 100644 index 0000000..2278f1f --- /dev/null +++ b/lib/webdriver/test/capabilities_test.js @@ -0,0 +1,72 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.jsunit'); +goog.require('webdriver.Capabilities'); + +function testSettingAndUnsettingACapability() { + var caps = new webdriver.Capabilities(); + assertNull(caps.get('foo')); + + caps.set('foo', 'bar'); + assertEquals('bar', caps.get('foo')); + + caps.set('foo', null); + assertNull(caps.get('foo')); +} + + +function testCheckingIfACapabilityIsSet() { + var caps = new webdriver.Capabilities(); + assertFalse(caps.has('foo')); + assertNull(caps.get('foo')); + + caps.set('foo', 'bar'); + assertTrue(caps.has('foo')); + + caps.set('foo', true); + assertTrue(caps.has('foo')); + + caps.set('foo', false); + assertFalse(caps.has('foo')); + assertFalse(caps.get('foo')); + + caps.set('foo', null); + assertFalse(caps.has('foo')); + assertNull(caps.get('foo')); +} + + +function testMergingCapabilities() { + var caps1 = new webdriver.Capabilities(). + set('foo', 'bar'). + set('color', 'red'); + + var caps2 = new webdriver.Capabilities(). + set('color', 'green'); + + assertEquals('bar', caps1.get('foo')); + assertEquals('red', caps1.get('color')); + assertEquals('green', caps2.get('color')); + assertNull(caps2.get('foo')); + + caps2.merge(caps1); + assertEquals('bar', caps1.get('foo')); + assertEquals('red', caps1.get('color')); + assertEquals('bar', caps2.get('foo')); + assertEquals('red', caps2.get('color')); +} diff --git a/lib/webdriver/test/events_test.js b/lib/webdriver/test/events_test.js new file mode 100644 index 0000000..41a86de --- /dev/null +++ b/lib/webdriver/test/events_test.js @@ -0,0 +1,203 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.jsunit'); +goog.require('webdriver.EventEmitter'); + +function testEmittingWhenNothingIsRegistered() { + var emitter = new webdriver.EventEmitter(); + emitter.emit('foo'); + // Ok if no errors are thrown. +} + +function testPassesArgsToListenersOnEmit() { + var emitter = new webdriver.EventEmitter(); + var now = goog.now(); + + var messages = []; + emitter.addListener('foo', function(arg) { messages.push(arg); }); + + emitter.emit('foo', now); + assertEquals(1, messages.length); + assertEquals(now, messages[0]); + + emitter.emit('foo', now + 15); + assertEquals(2, messages.length); + assertEquals(now, messages[0]); + assertEquals(now + 15, messages[1]); +} + +function testAddingListeners() { + var emitter = new webdriver.EventEmitter(); + var count = 0; + emitter.addListener('foo', function() { count++; }); + emitter.addListener('foo', function() { count++; }); + emitter.addListener('foo', function() { count++; }); + emitter.emit('foo'); + assertEquals(3, count); +} + +function testAddingOneShotListeners() { + var emitter = new webdriver.EventEmitter(); + var count = 0; + emitter.once('foo', function() { count++; }); + emitter.once('foo', function() { count++; }); + emitter.once('foo', function() { count++; }); + + emitter.emit('foo'); + assertEquals(3, count); + + emitter.emit('foo'); + assertEquals(3, count); +} + +function testCanOnlyAddAListenerConfigOnce() { + var emitter = new webdriver.EventEmitter(); + var count = 0; + function onFoo() { count++; } + + emitter.on('foo', onFoo); + emitter.on('foo', onFoo); + + emitter.emit('foo'); + assertEquals(1, count); + + emitter.emit('foo'); + assertEquals(2, count); +} + +function testAddingListenersWithCustomScope() { + var obj = { + count: 0, + inc: function() { + this.count++; + } + }; + var emitter = new webdriver.EventEmitter(); + emitter.addListener('foo', obj.inc, obj); + + emitter.emit('foo'); + assertEquals(1, obj.count); + + emitter.emit('foo'); + assertEquals(2, obj.count); + + emitter.once('bar', obj.inc, obj); + emitter.emit('bar'); + assertEquals(3, obj.count); + + emitter.emit('bar'); + assertEquals(3, obj.count); +} + +function testRemovingListeners() { + var emitter = new webdriver.EventEmitter(); + var count = 0; + emitter.addListener('foo', function() { count++; }); + emitter.addListener('foo', function() { count++; }); + + var toRemove = function() { count++; }; + emitter.addListener('foo', toRemove); + + emitter.emit('foo'); + assertEquals(3, count); + + emitter.removeListener('foo', toRemove); + emitter.emit('foo'); + assertEquals(5, count); +} + +function testRemovingAllListeners() { + var emitter = new webdriver.EventEmitter(); + var count = 0; + emitter.addListener('foo', function() { count++; }); + emitter.addListener('foo', function() { count++; }); + emitter.addListener('foo', function() { count++; }); + + emitter.emit('foo'); + assertEquals(3, count); + + emitter.removeAllListeners('foo'); + + emitter.emit('foo'); + assertEquals(3, count); +} + +function testRemovingAbsolutelyAllListeners() { + var emitter = new webdriver.EventEmitter(); + var messages = []; + emitter.addListener('foo', function() { messages.push('foo'); }); + emitter.addListener('bar', function() { messages.push('bar'); }); + + emitter.emit('foo'); + assertArrayEquals(['foo'], messages); + + emitter.emit('bar'); + assertArrayEquals(['foo', 'bar'], messages); + + emitter.emit('bar'); + emitter.emit('foo'); + assertArrayEquals(['foo', 'bar', 'bar', 'foo'], messages); + + emitter.removeAllListeners(); + assertArrayEquals(['foo', 'bar', 'bar', 'foo'], messages); +} + +function testListenerThatRemovesItself() { + var emitter = new webdriver.EventEmitter(); + emitter.on('foo', listener); + emitter.emit('foo'); + + function listener() { + emitter.removeListener('foo', listener); + } +} + +function testListenerThatRemovesItself_whenThereAreMultipleListeners() { + var emitter = new webdriver.EventEmitter(); + var messages = []; + emitter.on('foo', one); + emitter.on('foo', two); + emitter.emit('foo'); + assertArrayEquals(['one', 'two'], messages); + + function one() { + messages.push('one'); + emitter.removeListener('foo', one); + } + + function two() { + messages.push('two'); + } +} + +function testListenerAddsAnotherForTheSameEventType() { + var emitter = new webdriver.EventEmitter(); + var messages = []; + emitter.on('foo', one); + emitter.emit('foo'); + assertArrayEquals(['one', 'two'], messages); + + function one() { + messages.push('one'); + emitter.on('foo', two); + } + + function two() { + messages.push('two'); + } +} diff --git a/lib/webdriver/test/http/corsclient_test.js b/lib/webdriver/test/http/corsclient_test.js new file mode 100644 index 0000000..6289b24 --- /dev/null +++ b/lib/webdriver/test/http/corsclient_test.js @@ -0,0 +1,151 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.http.CorsClient'); +goog.require('webdriver.http.Request'); +goog.require('webdriver.test.testutil'); + +// Alias for readability. +var callbackHelper = webdriver.test.testutil.callbackHelper; + +function FakeXhr() {} +FakeXhr.prototype.status = 200; +FakeXhr.prototype.responseText = ''; +FakeXhr.prototype.withCredentials = false; +FakeXhr.prototype.open = function() {}; +FakeXhr.prototype.send = function() {}; +FakeXhr.prototype.setRequestHeader = function() {}; + +var URL = 'http://localhost:4444/wd/hub'; +var REQUEST = new webdriver.http.Request('GET', '/foo'); + +var control = new goog.testing.MockControl(); +var stubs = new goog.testing.PropertyReplacer(); +var mockClient, mockXhr; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + +function setUp() { + mockClient = control.createStrictMock(webdriver.http.Client); + mockXhr = control.createStrictMock(FakeXhr); + mockXhr.status = 200; + mockXhr.responseText = ''; + mockXhr.withCredentials = false; + setXhr(mockXhr); +} + +function tearDown() { + control.$tearDown(); + stubs.reset(); +} + +function setXhr(value) { + stubs.set(goog.global, 'XMLHttpRequest', function() { + return value; + }); + setXdr(); +} + +function setXdr(opt_value) { + stubs.set(goog.global, 'XDomainRequest', opt_value); +} + +function expectRequest(mockXhr) { + mockXhr.open('POST', URL + '/xdrpc', true); + return mockXhr.send(JSON.stringify({ + 'method': REQUEST.method, + 'path': REQUEST.path, + 'data': REQUEST.data + })); +} + +function testDetectsWhenCorsIsAvailable() { + setXhr(undefined); + assertFalse(webdriver.http.CorsClient.isAvailable()); + setXhr(); + assertFalse(webdriver.http.CorsClient.isAvailable()); + setXhr({withCredentials: null}); + assertFalse(webdriver.http.CorsClient.isAvailable()); + setXhr({withCredentials: true}); + assertTrue(webdriver.http.CorsClient.isAvailable()); + setXhr(); + setXdr(goog.nullFunction); + assertTrue(webdriver.http.CorsClient.isAvailable()); +} + +function testCorsClient_whenUnableToSendARequest() { + expectRequest(mockXhr).$does(function() { + mockXhr.onerror(); + }); + control.$replayAll(); + + var callback; + new webdriver.http.CorsClient(URL).send(REQUEST, + callback = callbackHelper(function(error) { + assertNotNullNorUndefined(error); + assertEquals(1, arguments.length); + })); + callback.assertCalled(); + control.$verifyAll(); +} + +function testCorsClient_handlesResponsesWithNoHeaders() { + expectRequest(mockXhr).$does(function() { + mockXhr.status = 200; + mockXhr.responseText = ''; + mockXhr.onload(); + }); + control.$replayAll(); + + var callback; + new webdriver.http.CorsClient(URL).send(REQUEST, + callback = callbackHelper(function(e, response) { + assertNull(e); + assertEquals(200, response.status); + assertEquals('', response.body); + + webdriver.test.testutil.assertObjectEquals({}, response.headers); + })); + callback.assertCalled(); + control.$verifyAll(); +} + +function testCorsClient_stripsNullCharactersFromResponseBody() { + expectRequest(mockXhr).$does(function() { + mockXhr.status = 200; + mockXhr.responseText = '\x00foo\x00\x00bar\x00'; + mockXhr.onload(); + }); + control.$replayAll(); + + var callback; + new webdriver.http.CorsClient(URL).send(REQUEST, + callback = callbackHelper(function(e, response) { + assertNull(e); + assertEquals(200, response.status); + assertEquals('foobar', response.body); + webdriver.test.testutil.assertObjectEquals({}, response.headers); + })); + callback.assertCalled(); + control.$verifyAll(); +} diff --git a/lib/webdriver/test/http/http_test.js b/lib/webdriver/test/http/http_test.js new file mode 100644 index 0000000..1b37ca3 --- /dev/null +++ b/lib/webdriver/test/http/http_test.js @@ -0,0 +1,428 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('bot.ErrorCode'); +goog.require('goog.Uri'); +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.Command'); +goog.require('webdriver.http.Client'); +goog.require('webdriver.http.Executor'); +goog.require('webdriver.promise'); +goog.require('webdriver.test.testutil'); + +// Alias for readability. +var callbackHelper = webdriver.test.testutil.callbackHelper; + +var control = new goog.testing.MockControl(); +var mockClient, executor, onCallback, onErrback; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + +function setUp() { + mockClient = control.createStrictMock(webdriver.http.Client); + + executor = new webdriver.http.Executor(mockClient); +} + +function tearDown() { + control.$tearDown(); +} + +function assertSuccess() { + onErrback.assertNotCalled('Did not expect errback'); + onCallback.assertCalled('Expected callback'); +} + +function assertFailure() { + onCallback.assertNotCalled('Did not expect callback'); + onErrback.assertCalled('Expected errback'); +} + +function headersToString(headers) { + var str = []; + for (var key in headers) { + str.push(key + ': ' + headers[key]); + } + return str.join('\n'); +} + +function expectRequest(method, path, data, headers) { + var description = method + ' ' + path + '\n' + headersToString(headers) + + '\n' + JSON.stringify(data); + + return mockClient.send(new goog.testing.mockmatchers.ArgumentMatcher( + function(request) { + assertEquals('wrong method', method, request.method); + assertEquals('wrong path', path + '', request.path); + webdriver.test.testutil.assertObjectEquals(data, request.data); + assertNull( + 'Wrong headers for request:\n' + description + + '\n Actual headers were:\n' + headersToString(request.headers), + goog.testing.asserts.findDifferences(headers, request.headers)); + return true; + }, description), + goog.testing.mockmatchers.isFunction); +} + +function response(status, headers, body) { + return new webdriver.http.Response(status, headers, body); +} + +function respondsWith(error, opt_response) { + return function(request, callback) { + callback(error, opt_response); + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Tests +// +/////////////////////////////////////////////////////////////////////////////// + +function testBuildPath() { + var parameters = {'sessionId':'foo', 'url':'http://www.google.com'}; + var finalPath = webdriver.http.Executor.buildPath_( + '/session/:sessionId/url', parameters); + assertEquals('/session/foo/url', finalPath); + webdriver.test.testutil.assertObjectEquals({'url':'http://www.google.com'}, + parameters); +} + +function testBuildPath_withWebElement() { + var parameters = {'sessionId':'foo', 'id': {}}; + parameters['id']['ELEMENT'] = 'bar'; + + var finalPath = webdriver.http.Executor.buildPath_( + '/session/:sessionId/element/:id/click', parameters); + assertEquals('/session/foo/element/bar/click', finalPath); + webdriver.test.testutil.assertObjectEquals({}, parameters); +} + +function testBuildPath_throwsIfMissingParameter() { + assertThrows(goog.partial(webdriver.http.Executor.buildPath_, + '/session/:sessionId', {})); + + assertThrows(goog.partial(webdriver.http.Executor.buildPath_, + '/session/:sessionId/element/:id', {'sessionId': 'foo'})); +} + +function testBuildPath_doesNotMatchOnSegmentsThatDoNotStartWithColon() { + assertEquals('/session/foo:bar/baz', + webdriver.http.Executor.buildPath_('/session/foo:bar/baz', {})); +} + +function testExecute_rejectsUnrecognisedCommands() { + assertThrows(goog.bind(executor.execute, executor, + new webdriver.Command('fake-command-name'), goog.nullFunction)); +} + +/** + * @param {!webdriver.Command} command The command to send. + * @param {!Function=} opt_onSuccess The function to check the response with. + */ +function assertSendsSuccessfully(command, opt_onSuccess) { + var callback; + executor.execute(command, callback = callbackHelper(function(e, response) { + assertNull(e); + assertNotNullNorUndefined(response); + if (opt_onSuccess) { + opt_onSuccess(response); + } + })); + callback.assertCalled(); + control.$verifyAll(); +} + +/** + * @param {!webdriver.Command} command The command to send. + * @param {!Function=} opt_onError The function to check the error with. + */ +function assertFailsToSend(command, opt_onError) { + var callback; + executor.execute(command, callback = callbackHelper(function(e, response) { + assertNotNullNorUndefined(e); + assertUndefined(response); + if (opt_onError) { + opt_onError(e); + } + })); + callback.assertCalled(); + control.$verifyAll(); +} + +function testExecute_clientFailsToSendRequest() { + var error = new Error('boom'); + expectRequest('POST', '/session', {}, { + 'Accept': 'application/json; charset=utf-8' + }). + $does(respondsWith(error)); + control.$replayAll(); + + assertFailsToSend(new webdriver.Command(webdriver.CommandName.NEW_SESSION), + function(e) { + assertEquals(error, e); + }); +} + +function testExecute_commandWithNoUrlParameters() { + expectRequest('POST', '/session', {}, { + 'Accept': 'application/json; charset=utf-8' + }). + $does(respondsWith(null, response(200, {}, ''))); + control.$replayAll(); + + assertSendsSuccessfully( + new webdriver.Command(webdriver.CommandName.NEW_SESSION)); +} + +function testExecute_rejectsCommandsMissingUrlParameters() { + var command = + new webdriver.Command(webdriver.CommandName.FIND_CHILD_ELEMENT). + setParameter('sessionId', 's123'). + // Let this be missing: setParameter('id', {'ELEMENT': 'e456'}). + setParameter('using', 'id'). + setParameter('value', 'foo'); + + control.$replayAll(); + assertThrows(goog.bind(executor.execute, executor, command)); + control.$verifyAll(); +} + +function testExecute_replacesUrlParametersWithCommandParameters() { + var command = + new webdriver.Command(webdriver.CommandName.GET). + setParameter('sessionId', 's123'). + setParameter('url', 'http://www.google.com'); + + expectRequest('POST', '/session/s123/url', + {'url': 'http://www.google.com'}, + {'Accept': 'application/json; charset=utf-8'}). + $does(respondsWith(null, response(200, {}, ''))); + control.$replayAll(); + + assertSendsSuccessfully(command); +} + +function testExecute_returnsParsedJsonResponse() { + var responseObj = { + 'status': bot.ErrorCode.SUCCESS, + 'value': 'http://www.google.com' + }; + var command = new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL). + setParameter('sessionId', 's123'); + + expectRequest('GET', '/session/s123/url', {}, { + 'Accept': 'application/json; charset=utf-8' + }).$does(respondsWith(null, + response(200, {'Content-Type': 'application/json'}, + JSON.stringify(responseObj)))); + control.$replayAll(); + + assertSendsSuccessfully(command, function(response) { + webdriver.test.testutil.assertObjectEquals(responseObj, response); + }); +} + +function testExecute_returnsSuccessFor2xxWithBodyAsValueWhenNotJson() { + var command = new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL). + setParameter('sessionId', 's123'); + + expectRequest('GET', '/session/s123/url', {}, { + 'Accept': 'application/json; charset=utf-8' + }).$does(respondsWith(null, + response(200, {}, 'hello, world\r\ngoodbye, world!'))); + control.$replayAll(); + + assertSendsSuccessfully(command, function(response) { + webdriver.test.testutil.assertObjectEquals({ + 'status': bot.ErrorCode.SUCCESS, + 'value': 'hello, world\ngoodbye, world!' + }, response); + }); +} + +function testExecute_returnsSuccessFor2xxInvalidJsonBody() { + var invalidJson = '['; + expectRequest('POST', '/session', {}, { + 'Accept': 'application/json; charset=utf-8' + }). + $does(respondsWith(null, response(200, { + 'Content-Type': 'application/json' + }, invalidJson))); + control.$replayAll(); + + assertSendsSuccessfully( + new webdriver.Command(webdriver.CommandName.NEW_SESSION), + function(response) { + webdriver.test.testutil.assertObjectEquals({ + 'status': bot.ErrorCode.SUCCESS, + 'value': invalidJson + }, response); + }); +} + +function testExecute_returnsUnknownCommandFor404WithBodyAsValueWhenNotJson() { + var command = new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL). + setParameter('sessionId', 's123'); + + expectRequest('GET', '/session/s123/url', {}, { + 'Accept': 'application/json; charset=utf-8' + }).$does(respondsWith(null, + response(404, {}, 'hello, world\r\ngoodbye, world!'))); + control.$replayAll(); + + assertSendsSuccessfully(command, function(response) { + webdriver.test.testutil.assertObjectEquals({ + 'status': bot.ErrorCode.UNKNOWN_COMMAND, + 'value': 'hello, world\ngoodbye, world!' + }, response); + }); +} + +function testExecute_returnsUnknownErrorForGenericErrorCodeWithBodyAsValueWhenNotJson() { + var command = new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL). + setParameter('sessionId', 's123'); + + expectRequest('GET', '/session/s123/url', {}, { + 'Accept': 'application/json; charset=utf-8' + }).$does(respondsWith(null, + response(500, {}, 'hello, world\r\ngoodbye, world!'))); + control.$replayAll(); + + assertSendsSuccessfully(command, function(response) { + webdriver.test.testutil.assertObjectEquals({ + 'status': bot.ErrorCode.UNKNOWN_ERROR, + 'value': 'hello, world\ngoodbye, world!' + }, response); + }); +} + +function testExecute_attemptsToParseBodyWhenNoContentTypeSpecified() { + var responseObj = { + 'status': bot.ErrorCode.SUCCESS, + 'value': 'http://www.google.com' + }; + var command = new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL). + setParameter('sessionId', 's123'); + + expectRequest('GET', '/session/s123/url', {}, { + 'Accept': 'application/json; charset=utf-8' + }).$does(respondsWith(null, + response(200, {}, JSON.stringify(responseObj)))); + control.$replayAll(); + + assertSendsSuccessfully(command, function(response) { + webdriver.test.testutil.assertObjectEquals(responseObj, response); + }); +} + +function testCanDefineNewCommands() { + executor.defineCommand('greet', 'GET', '/person/:name'); + + var command = new webdriver.Command('greet'). + setParameter('name', 'Bob'); + + expectRequest('GET', '/person/Bob', {}, + {'Accept': 'application/json; charset=utf-8'}). + $does(respondsWith(null, response(200, {}, ''))); + control.$replayAll(); + + assertSendsSuccessfully(command); +} + +function testCanRedefineStandardCommands() { + executor.defineCommand(webdriver.CommandName.GO_BACK, + 'POST', '/custom/back'); + + var command = new webdriver.Command(webdriver.CommandName.GO_BACK). + setParameter('times', 3); + + expectRequest('POST', '/custom/back', + {'times': 3}, + {'Accept': 'application/json; charset=utf-8'}). + $does(respondsWith(null, response(200, {}, ''))); + control.$replayAll(); + + assertSendsSuccessfully(command); +} + +function FakeXmlHttpRequest(headers, status, responseText) { + return { + getAllResponseHeaders: function() { return headers; }, + status: status, + responseText: responseText + }; +} + +function testXmlHttpRequestToHttpResponse_parseHeaders_windows() { + var response = webdriver.http.Response.fromXmlHttpRequest( + FakeXmlHttpRequest([ + 'a:b', + 'c: d', + 'e :f', + 'g : h' + ].join('\r\n'), 200, '')); + assertEquals(200, response.status); + assertEquals('', response.body); + + webdriver.test.testutil.assertObjectEquals({ + 'a': 'b', + 'c': 'd', + 'e': 'f', + 'g': 'h' + }, response.headers); +} + +function testXmlHttpRequestToHttpResponse_parseHeaders_unix() { + var response = webdriver.http.Response.fromXmlHttpRequest( + FakeXmlHttpRequest([ + 'a:b', + 'c: d', + 'e :f', + 'g : h' + ].join('\n'), 200, '')); + assertEquals(200, response.status); + assertEquals('', response.body); + + webdriver.test.testutil.assertObjectEquals({ + 'a': 'b', + 'c': 'd', + 'e': 'f', + 'g': 'h' + }, response.headers); +} + +function testXmlHttpRequestToHttpResponse_noHeaders() { + var response = webdriver.http.Response.fromXmlHttpRequest( + FakeXmlHttpRequest('', 200, '')); + assertEquals(200, response.status); + assertEquals('', response.body); + webdriver.test.testutil.assertObjectEquals({}, response.headers); +} + +function testXmlHttpRequestToHttpResponse_stripsNullCharactersFromBody() { + var response = webdriver.http.Response.fromXmlHttpRequest( + FakeXmlHttpRequest('', 200, '\x00\0foo\x00\x00bar\x00\0')); + assertEquals(200, response.status); + assertEquals('foobar', response.body); + webdriver.test.testutil.assertObjectEquals({}, response.headers); +} diff --git a/lib/webdriver/test/http/xhrclient_test.js b/lib/webdriver/test/http/xhrclient_test.js new file mode 100644 index 0000000..9f2c3db --- /dev/null +++ b/lib/webdriver/test/http/xhrclient_test.js @@ -0,0 +1,196 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.http.Request'); +goog.require('webdriver.http.XhrClient'); +goog.require('webdriver.promise'); +goog.require('webdriver.test.testutil'); + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + +// Alias for readability. +var callbackHelper = webdriver.test.testutil.callbackHelper; + +function FakeXhr() {} +FakeXhr.prototype.status = 200; +FakeXhr.prototype.responseText = ''; +FakeXhr.prototype.open = function() {}; +FakeXhr.prototype.send = function() {}; +FakeXhr.prototype.getAllResponseHeaders = function() {}; +FakeXhr.prototype.setRequestHeader = function() {}; + +var URL = 'http://localhost:4444/wd/hub'; +var REQUEST = new webdriver.http.Request('GET', '/foo'); + +var control = new goog.testing.MockControl(); +var stubs = new goog.testing.PropertyReplacer(); +var mockClient, mockXhr; + +function setUp() { + mockClient = control.createStrictMock(webdriver.http.Client); + mockXhr = control.createStrictMock(FakeXhr); + stubs.set(goog.global, 'XMLHttpRequest', function() { + return mockXhr; + }); +} + +function tearDown() { + control.$tearDown(); + stubs.reset(); +} + +function setXhr(value) { + stubs.set(goog.global, 'XMLHttpRequest', value); +} + +function expectRequest(mockXhr) { + mockXhr.open(REQUEST.method, URL + REQUEST.path, true); + for (var header in REQUEST.headers) { + mockXhr.setRequestHeader(header, REQUEST.headers[header]); + } + return mockXhr.send(JSON.stringify(REQUEST.data)); +} + +function testXhrClient_whenUnableToSendARequest() { + expectRequest(mockXhr).$does(function() { + mockXhr.onerror(); + }); + control.$replayAll(); + + var callback; + new webdriver.http.XhrClient(URL).send(REQUEST, + callback = callbackHelper(function(error) { + assertNotNullNorUndefined(error); + assertEquals(1, arguments.length); + })); + callback.assertCalled(); + control.$verifyAll(); +} + +function testXhrClient_parsesResponseHeaders_windows() { + expectRequest(mockXhr).$does(function() { + mockXhr.status = 200; + mockXhr.responseText = ''; + mockXhr.onload(); + }); + mockXhr.getAllResponseHeaders().$returns([ + 'a:b', + 'c: d', + 'e :f', + 'g : h' + ].join('\r\n')); + control.$replayAll(); + + var callback; + new webdriver.http.XhrClient(URL).send(REQUEST, + callback = callbackHelper(function(e, response) { + assertNull(e); + + assertEquals(200, response.status); + assertEquals('', response.body); + + webdriver.test.testutil.assertObjectEquals({ + 'a': 'b', + 'c': 'd', + 'e': 'f', + 'g': 'h' + }, response.headers); + })); + callback.assertCalled(); + control.$verifyAll(); +} + +function testXhrClient_parsesResponseHeaders_unix() { + expectRequest(mockXhr).$does(function() { + mockXhr.status = 200; + mockXhr.responseText = ''; + mockXhr.onload(); + }); + mockXhr.getAllResponseHeaders().$returns([ + 'a:b', + 'c: d', + 'e :f', + 'g : h' + ].join('\n')); + control.$replayAll(); + + var callback; + new webdriver.http.XhrClient(URL).send(REQUEST, + callback = callbackHelper(function(e, response) { + assertNull(e); + assertEquals(200, response.status); + assertEquals('', response.body); + + webdriver.test.testutil.assertObjectEquals({ + 'a': 'b', + 'c': 'd', + 'e': 'f', + 'g': 'h' + }, response.headers); + })); + callback.assertCalled(); + control.$verifyAll(); +} + +function testXhrClient_handlesResponsesWithNoHeaders() { + expectRequest(mockXhr).$does(function() { + mockXhr.status = 200; + mockXhr.responseText = ''; + mockXhr.onload(); + }); + mockXhr.getAllResponseHeaders().$returns(''); + control.$replayAll(); + + var callback; + new webdriver.http.XhrClient(URL).send(REQUEST, + callback = callbackHelper(function(e, response) { + assertNull(e); + assertEquals(200, response.status); + assertEquals('', response.body); + + webdriver.test.testutil.assertObjectEquals({}, response.headers); + })); + callback.assertCalled(); + control.$verifyAll(); +} + +function testXhrClient_stripsNullCharactersFromResponseBody() { + expectRequest(mockXhr).$does(function() { + mockXhr.status = 200; + mockXhr.responseText = '\x00foo\x00\x00bar\x00'; + mockXhr.onload(); + }); + mockXhr.getAllResponseHeaders().$returns(''); + control.$replayAll(); + + var callback; + new webdriver.http.XhrClient(URL).send(REQUEST, + callback = callbackHelper(function(e, response) { + assertNull(e); + assertEquals(200, response.status); + assertEquals('foobar', response.body); + webdriver.test.testutil.assertObjectEquals({}, response.headers); + })); + callback.assertCalled(); + control.$verifyAll(); +} diff --git a/lib/webdriver/test/locators_test.js b/lib/webdriver/test/locators_test.js new file mode 100644 index 0000000..21e6479 --- /dev/null +++ b/lib/webdriver/test/locators_test.js @@ -0,0 +1,59 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.jsunit'); +goog.require('webdriver.By'); +goog.require('webdriver.Locator'); +goog.require('webdriver.Locator.Strategy'); +goog.require('webdriver.test.testutil'); + +// By is exported by webdriver.By, but IDEs don't recognize +// goog.exportSymbol. Explicitly define it here to make the +// IDE stop complaining. +var By = webdriver.By; + +var TARGET = 'some-value'; + +function testCheckLocator() { + function assertLocatorTypeAndTarget(expectedLocator, locator) { + assertEquals('Wrong type', expectedLocator.using, locator.using); + assertEquals('Wrong target', expectedLocator.value, locator.value); + } + + + for (var prop in webdriver.Locator.Strategy) { + var obj = {}; + obj[prop] = TARGET; + assertLocatorTypeAndTarget( + webdriver.Locator.Strategy[prop](TARGET), + webdriver.Locator.checkLocator(obj)); + assertLocatorTypeAndTarget( + webdriver.Locator.Strategy[prop](TARGET), + webdriver.Locator.checkLocator(By[prop](TARGET))); + } + + assertEquals( + 'Should accept custom locator functions', + goog.nullFunction, + webdriver.Locator.checkLocator(goog.nullFunction)); +} + +function testToString() { + assertEquals('By.id("foo")', By.id('foo').toString()); + assertEquals('By.className("foo")', By.className('foo').toString()); + assertEquals('By.linkText("foo")', By.linkText('foo').toString()); +} diff --git a/lib/webdriver/test/logging_test.js b/lib/webdriver/test/logging_test.js new file mode 100644 index 0000000..bb542f9 --- /dev/null +++ b/lib/webdriver/test/logging_test.js @@ -0,0 +1,85 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.debug.LogRecord'); +goog.require('goog.debug.Logger'); +goog.require('goog.testing.jsunit'); +goog.require('webdriver.logging'); + +function convert(level, msg, name, time) { + var recordIn = new webdriver.logging.LogRecord(level, msg, name, time); + return webdriver.logging.Entry.fromClosureLogRecord(recordIn); +} + +function checkRecord(record, level, msg, time) { + assertEquals('wrong level', level.value, record.level.value); + assertEquals('wrong message', msg, record.message); + assertEquals('wrong time', time, record.timestamp); +} + +function testPreferencesToJSON() { + var prefs = new webdriver.logging.Preferences(); + assertObjectEquals({}, prefs.toJSON()); + + prefs.setLevel('foo', webdriver.logging.Level.DEBUG); + assertObjectEquals({'foo': 'DEBUG'}, prefs.toJSON()); + + prefs.setLevel('bar', webdriver.logging.Level.OFF); + prefs.setLevel('baz', webdriver.logging.Level.WARNING); + assertObjectEquals( + {'foo': 'DEBUG', 'bar': 'OFF', 'baz': 'WARNING'}, + prefs.toJSON()); + + // CONFIG should always map to DEBUG. + prefs.setLevel('quux', webdriver.logging.Level.CONFIG); + assertObjectEquals( + {'foo': 'DEBUG', 'bar': 'OFF', 'baz': 'WARNING', 'quux': 'DEBUG'}, + prefs.toJSON()); + + prefs.setLevel('quot', webdriver.logging.Level.ALL); + assertObjectEquals( + {'foo': 'DEBUG', 'bar': 'OFF', 'baz': 'WARNING', 'quux': 'DEBUG', + 'quot': 'ALL'}, + prefs.toJSON()); +} + +function testConvertingLogRecords() { + checkRecord( + convert(goog.debug.Logger.Level.SHOUT, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.SEVERE, '[the.name] foo bar', 1234); + checkRecord( + convert(goog.debug.Logger.Level.SEVERE, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.SEVERE, '[the.name] foo bar', 1234); + checkRecord( + convert(goog.debug.Logger.Level.WARNING, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.WARNING, '[the.name] foo bar', 1234); + checkRecord( + convert(goog.debug.Logger.Level.INFO, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.INFO, '[the.name] foo bar', 1234); + checkRecord( + convert(goog.debug.Logger.Level.CONFIG, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.DEBUG, '[the.name] foo bar', 1234); + checkRecord( + convert(goog.debug.Logger.Level.FINE, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.DEBUG, '[the.name] foo bar', 1234); + checkRecord( + convert(goog.debug.Logger.Level.FINER, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.DEBUG, '[the.name] foo bar', 1234); + checkRecord( + convert(goog.debug.Logger.Level.FINEST, 'foo bar', 'the.name', 1234), + webdriver.logging.Level.DEBUG, '[the.name] foo bar', 1234); +} diff --git a/lib/webdriver/test/process_test.js b/lib/webdriver/test/process_test.js new file mode 100644 index 0000000..c8f7bf6 --- /dev/null +++ b/lib/webdriver/test/process_test.js @@ -0,0 +1,72 @@ +// Copyright 2014 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. + +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('webdriver.process'); +goog.require('webdriver.test.testutil'); + +var stubs = new goog.testing.PropertyReplacer(); + +function tearDown() { + stubs.reset(); +} + +function initProcess(windowObject) { + stubs.set(webdriver.process, 'PROCESS_', + webdriver.process.initBrowserProcess_(windowObject)); +} + +function testInitializesEnvironmentVariablesFromLocation() { + initProcess({location: '?a&b=123&c=456&c=789'}); + assertEquals('', webdriver.process.getEnv('a')); + assertEquals('123', webdriver.process.getEnv('b')); + assertEquals('["456","789"]', webdriver.process.getEnv('c')); + assertUndefined(webdriver.process.getEnv('not-there')); +} + +function testSettingEnvironmentVariables() { + initProcess({}); + assertUndefined(webdriver.process.getEnv('foo')); + webdriver.process.setEnv('foo', 'bar'); + assertEquals('bar', webdriver.process.getEnv('foo')); +} + +function testCoercesNewEnvironmentVariablesToAString() { + initProcess({}); + assertUndefined(webdriver.process.getEnv('foo')); + webdriver.process.setEnv('foo', goog.nullFunction); + assertEquals(goog.nullFunction + '', webdriver.process.getEnv('foo')); + + assertUndefined(webdriver.process.getEnv('bar')); + webdriver.process.setEnv('bar', 123); + assertEquals('123', webdriver.process.getEnv('bar')); +} + +function testCanUnsetEnvironmentVariables() { + initProcess({}); + assertUndefined(webdriver.process.getEnv('foo')); + + webdriver.process.setEnv('foo', 'one'); + assertEquals('one', webdriver.process.getEnv('foo')); + + webdriver.process.setEnv('foo'); + assertUndefined(webdriver.process.getEnv('foo')); + + webdriver.process.setEnv('foo', 'two'); + assertEquals('two', webdriver.process.getEnv('foo')); + + webdriver.process.setEnv('foo', null); + assertUndefined(webdriver.process.getEnv('foo')); +} diff --git a/lib/webdriver/test/promise_error_test.js b/lib/webdriver/test/promise_error_test.js new file mode 100644 index 0000000..ef2060b --- /dev/null +++ b/lib/webdriver/test/promise_error_test.js @@ -0,0 +1,892 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Contains tests against promise error handling. Many tests use + * goog.Promise to control test termination independent of webdriver.promise + * (and notably webdriver.promise.ControlFlow). + */ + +'use strict'; + +goog.require('goog.Promise'); +goog.require('goog.async.run'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('goog.userAgent.product'); +goog.require('webdriver.promise'); +goog.require('webdriver.test.testutil'); + + + +var StubError = webdriver.test.testutil.StubError, + throwStubError = webdriver.test.testutil.throwStubError, + assertIsStubError = webdriver.test.testutil.assertIsStubError; + + +var flow, uncaughtExceptions; + + +function longStackTracesAreBroken() { + // Safari 8.0 is "Safari/538.35.8" in the user agent. + return goog.userAgent.product.SAFARI && + !goog.userAgent.isVersionOrHigher(538); +} + + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + +function setUp() { + flow = webdriver.promise.controlFlow(); + uncaughtExceptions = []; + flow.on('uncaughtException', onUncaughtException); +} + + +function tearDown() { + return waitForIdle(flow).then(function() { + assertArrayEquals('There were uncaught exceptions', + [], uncaughtExceptions); + flow.reset(); + }); +} + + +function onUncaughtException(e) { + uncaughtExceptions.push(e); +} + + +function waitForAbort(opt_flow, opt_n) { + var n = opt_n || 1; + var theFlow = opt_flow || flow; + theFlow.removeAllListeners( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new goog.Promise(function(fulfill, reject) { + theFlow.once('idle', function() { + reject(Error('expected flow to report an unhandled error')); + }); + + var errors = []; + theFlow.on('uncaughtException', onError); + function onError(e) { + errors.push(e); + if (errors.length === n) { + theFlow.removeListener('uncaughtException', onError); + fulfill(n === 1 ? errors[0] : errors); + } + } + }); +} + + +function waitForIdle(opt_flow) { + var theFlow = opt_flow || flow; + return new goog.Promise(function(fulfill, reject) { + if (theFlow.isIdle()) { + fulfill(); + return; + } + theFlow.once('idle', fulfill); + theFlow.once('uncaughtException', reject); + }); +} + + +function testRejectedPromiseTriggersErrorCallback() { + return webdriver.promise.rejected(new StubError). + then(fail, assertIsStubError); +} + + +function testCallbackThrowsTriggersSubsequentErrorCallback_fulfilledPromise() { + return webdriver.promise.fulfilled(). + then(throwStubError). + then(fail, assertIsStubError); +} + + +function testCallbackThrowsTriggersSubsequentErrorCallback_rejectedPromise() { + var e = Error('not the droids you are looking for'); + return webdriver.promise.rejected(e). + then(fail, throwStubError). + then(fail, assertIsStubError); +} + + +function +testCallbackReturnsRejectedPromiseTriggersSubsequentErrback_fulfilled() { + return webdriver.promise.fulfilled().then(function() { + return webdriver.promise.rejected(new StubError); + }).then(fail, assertIsStubError); +} + + +function +testCallbackReturnsRejectedPromiseTriggersSubsequentErrback_rejected() { + var e = Error('not the droids you are looking for'); + return webdriver.promise.rejected(e). + then(fail, function() { + return webdriver.promise.rejected(new StubError); + }). + then(fail, assertIsStubError); +} + + +function testReportsUnhandledRejectionsThroughTheControlFlow() { + webdriver.promise.rejected(new StubError); + return waitForAbort().then(assertIsStubError); +} + + +function testMultipleUnhandledRejectionsOutsideATask_reportedInOrderOccurred() { + var e1 = Error('error 1'); + var e2 = Error('error 2'); + + webdriver.promise.rejected(e1); + webdriver.promise.rejected(e2); + + return waitForAbort(flow).then(function(error) { + assertTrue( + error instanceof webdriver.promise.MultipleUnhandledRejectionError); + // TODO: switch to Array.from when we drop node 0.12.x + var errors = []; + for (var e of error.errors) { + errors.push(e); + } + assertArrayEquals([e1, e2], errors); + }); +} + + +function testDoesNotReportUnhandledErrorIfHandlerAddedBeforeNextTick() { + var promise = webdriver.promise.rejected(new StubError); + promise.then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testDoesNotReportUnhandledErrorIfHandlerAddedAsyncBeforeReport() { + var called = false; + return new goog.Promise(function(fulfill, reject) { + var promise; + goog.async.run(function() { + promise.then(fail, function(e) { + called = true; + assertIsStubError(e); + }); + waitForIdle().then(fulfill, reject); + }); + promise = webdriver.promise.rejected(new StubError); + }).then(function() { + assertTrue(called); + }) +} + + +function testTaskThrows() { + return flow.execute(throwStubError).then(fail, assertIsStubError); +} + + +function testTaskReturnsRejectedPromise() { + return flow.execute(function() { + return webdriver.promise.rejected(new StubError) + }).then(fail, assertIsStubError); +} + + +function testTaskHasUnhandledRejection() { + return flow.execute(function() { + webdriver.promise.rejected(new StubError) + }).then(fail, assertIsStubError); +} + + +function testTaskFails_returnedPromiseIsUnhandled() { + flow.execute(throwStubError); + return waitForAbort().then(assertIsStubError); +} + + +function testTaskHasUnhandledRejection_cancelsRemainingSubTasks() { + var seen = []; + flow.execute(function() { + webdriver.promise.rejected(new StubError); + + flow.execute(() => seen.push('a')) + .then(() => seen.push('b'), (e) => seen.push(e)); + flow.execute(() => seen.push('c')) + .then(() => seen.push('b'), (e) => seen.push(e)); + }); + + return waitForAbort() + .then(assertIsStubError) + .then(() => assertArrayEquals([], seen)); +} + + +function testSubTaskFails_caughtByParentTask() { + return flow.execute(function() { + flow.execute(throwStubError); + }).then(fail, assertIsStubError); +} + + +function testSubTaskFails_uncaughtErrorBubblesUpToFlow() { + flow.execute(function() { + flow.execute(throwStubError); + }); + return waitForAbort().then(assertIsStubError); +} + + +function testSubTasilFails_cancelsRemainingSubTasks() { + var seen = []; + flow.execute(function() { + flow.execute(() => webdriver.promise.rejected(new StubError)); + flow.execute(() => seen.push('a')) + .then(() => seen.push('b'), (e) => seen.push(e)); + flow.execute(() => seen.push('c')) + .then(() => seen.push('b'), (e) => seen.push(e)); + }); + + return waitForAbort() + .then(assertIsStubError) + .then(() => assertArrayEquals([], seen)); +} + + +function testNestedTaskFails_returnsUpToParent() { + return flow.execute(function() { + return flow.execute(function() { + return flow.execute(throwStubError); + }); + }).then(fail, assertIsStubError); +} + + +function testNestedTaskFails_uncaughtErrorBubblesUp_taskThrows() { + flow.execute(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }); + return waitForAbort().then(assertIsStubError); +} + + +function testNestedTaskFails_uncaughtErrorBubblesUp_taskThrows_caughtAtRoot() { + flow.execute(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }).then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testNestedTaskFails_uncaughtErrorBubblesUp_promise() { + flow.execute(function() { + flow.execute(function() { + flow.execute(function() { + webdriver.promise.rejected(new StubError); + }); + }); + }); + return waitForAbort().then(assertIsStubError); +} + + +function testNestedTaskFails_uncaughtErrorBubblesUp_promise_caughtAtRoot() { + flow.execute(function() { + flow.execute(function() { + webdriver.promise.rejected(new StubError); + }); + }).then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testNestedTaskFails_mixtureOfHangingAndFreeSubTasks() { + flow.execute(function() { + return flow.execute(function() { + flow.execute(throwStubError); + }); + }); + return waitForAbort().then(assertIsStubError); +} + + +function testTaskReturnsPromiseLikeObjectThatInvokesErrback() { + return flow.execute(function() { + return { + 'then': function(_, errback) { + errback('abc123'); + } + }; + }).then(fail, function(value) { + assertEquals('abc123', value); + }); +} + + +function testWaitConditionThrows_waitFailureIsCaught() { + return flow.wait(throwStubError, 50).then(fail, assertIsStubError); +} + + +function testWaitConditionThrows_waitFailureIsNotCaught() { + flow.wait(throwStubError, 50); + return waitForAbort().then(assertIsStubError); +} + + +function testWaitConditionReturnsRejectedPromise_waitFailureIsCaught() { + return flow.wait(function() { + return webdriver.promise.rejected(new StubError); + }, 50).then(fail, assertIsStubError); +} + + +function testWaitConditionReturnsRejectedPromise_waitFailureIsNotCaught() { + flow.wait(function() { + return webdriver.promise.rejected(new StubError); + }, 50); + return waitForAbort().then(assertIsStubError); +} + + +function testWaitConditionHasUnhandledPromiseRejection_waitFailureCaught() { + return flow.wait(function() { + webdriver.promise.rejected(new StubError); + }, 50).then(fail, assertIsStubError); +} + + +function testWaitConditionHasUnhandledPromiseRejection_waitFailureNotCaught() { + flow.wait(function() { + webdriver.promise.rejected(new StubError); + }, 50); + return waitForAbort().then(assertIsStubError); +} + + +function testWaitConditionHasSubTaskFailure_caughtByWait() { + return flow.wait(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }, 50).then(fail, assertIsStubError); +} + + +function testWaitConditionHasSubTaskFailure_notCaughtByWait() { + flow.wait(function() { + flow.execute(function() { + flow.execute(throwStubError); + }); + }, 50); + return waitForAbort().then(assertIsStubError); +} + + +function testErrbackMayThrowANewError_startWithNormalPromise() { + var error = Error('an error'); + return webdriver.promise.rejected(error). + thenCatch(function(e) { + assertEquals(e, error); + throw new StubError; + }). + thenCatch(assertIsStubError); +} + + +function testErrbackMayThrowANewError_startWithTaskResult() { + var error = Error('an error'); + return flow.execute(function() { + throw error; + }). + thenCatch(function(e) { + assertEquals(e, error); + throw new StubError; + }). + thenCatch(assertIsStubError); +} + + +function testErrbackMayThrowANewError_uncaught_startWithNormalPromise() { + var error = Error('an error'); + webdriver.promise.rejected(error). + thenCatch(function(e) { + assertEquals(e, error); + throw new StubError; + }); + return waitForAbort().then(assertIsStubError); +} + + +function testErrbackMayThrowANewError_uncaught_startWithTaskResult() { + var error = Error('an error'); + flow.execute(function() { + throw error; + }). + thenCatch(function(e) { + assertEquals(e, error); + throw new StubError; + }); + return waitForAbort().then(assertIsStubError); +} + + +function testThrownPromiseIsHandledSameAsReturningPromise_promiseIsFulfilled() { + return webdriver.promise.fulfilled().then(function() { + throw webdriver.promise.fulfilled(1234); + }).then(function(value) { + assertEquals(1234, value); + }); +} + + +function testTaskThrowsPromise_promiseWasFulfiled() { + var toThrow = webdriver.promise.fulfilled(1234); + flow.execute(function() { + throw toThrow; + }).then(fail, function(value) { + assertEquals(toThrow, value); + return toThrow; + }).then(function(value) { + assertEquals(1234, value); + }); + return waitForIdle(); +} + + +function testTaskThrowsPromise_promiseWasRejected() { + var toThrow = webdriver.promise.rejected(new StubError); + toThrow.thenCatch(goog.nullFunction); // For tearDown. + flow.execute(function() { + throw toThrow; + }).then(fail, function(e) { + assertEquals(toThrow, e); + return e; + }).then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testFailsTaskIfThereIsAnUnhandledErrorWhileWaitingOnTaskResult() { + var d = webdriver.promise.defer(); + flow.execute(function() { + webdriver.promise.rejected(new StubError); + return d.promise; + }).then(fail, assertIsStubError); + + return waitForIdle().then(function() { + return d.promise; + }).then(fail, function(e) { + assertEquals('CancellationError: StubError', e.toString()); + }); +} + + +function testFailsParentTaskIfAsyncScheduledTaskFails() { + var d = webdriver.promise.defer(); + flow.execute(function() { + flow.execute(throwStubError); + return d.promise; + }).then(fail, assertIsStubError); + + return waitForIdle().then(function() { + return d.promise; + }).then(fail, function(e) { + assertEquals('CancellationError: StubError', e.toString()); + }); +} + + +function testLongStackTraces_alwaysIncludesTaskStacksInFailures() { + if (longStackTracesAreBroken()) { + return; + } + + webdriver.promise.LONG_STACK_TRACES = false; + flow.execute(function() { + flow.execute(function() { + flow.execute(throwStubError, 'throw error'); + }, 'two'); + }, 'three'). + then(fail, function(e) { + assertIsStubError(e); + if (!goog.isString(e.stack)) { + return; + } + var messages = goog.array.filter( + webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { + return /^From: /.test(line); + }); + assertArrayEquals([ + 'From: Task: throw error', + 'From: Task: two', + 'From: Task: three' + ], messages); + }); + return waitForIdle(); +} + + +function testLongStackTraces_doesNotIncludeCompletedTasks() { + if (longStackTracesAreBroken()) { + return; + } + + flow.execute(goog.nullFunction, 'succeeds'); + flow.execute(throwStubError, 'kaboom').then(fail, function(e) { + assertIsStubError(e); + if (!goog.isString(e.stack)) { + return; + } + var messages = goog.array.filter( + webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { + return /^From: /.test(line); + }); + assertArrayEquals(['From: Task: kaboom'], messages); + }); + return waitForIdle(); +} + + +function testLongStackTraces_doesNotIncludePromiseChainWhenDisabled() { + if (longStackTracesAreBroken()) { + return; + } + + webdriver.promise.LONG_STACK_TRACES = false; + flow.execute(function() { + flow.execute(function() { + return webdriver.promise.fulfilled(). + then(goog.nullFunction). + then(goog.nullFunction). + then(throwStubError); + }, 'eventually fails'); + }, 'start'). + then(fail, function(e) { + assertIsStubError(e); + if (!goog.isString(e.stack)) { + return; + } + var messages = goog.array.filter( + webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { + return /^From: /.test(line); + }); + assertArrayEquals([ + 'From: Task: eventually fails', + 'From: Task: start' + ], messages); + }); + return waitForIdle(); +} + + +function testLongStackTraces_includesPromiseChainWhenEnabled() { + if (longStackTracesAreBroken()) { + return; + } + + webdriver.promise.LONG_STACK_TRACES = true; + flow.execute(function() { + flow.execute(function() { + return webdriver.promise.fulfilled(). + then(goog.nullFunction). + then(goog.nullFunction). + then(throwStubError); + }, 'eventually fails'); + }, 'start'). + then(fail, function(e) { + assertIsStubError(e); + if (!goog.isString(e.stack)) { + return; + } + var messages = goog.array.filter( + webdriver.stacktrace.getStack(e).split(/\n/), function(line, index) { + return /^From: /.test(line); + }); + assertArrayEquals([ + 'From: Promise: then', + 'From: Task: eventually fails', + 'From: Task: start' + ], messages); + }); + return waitForIdle(); +} + + +function testFrameCancelsRemainingTasks_onUnhandledTaskFailure() { + var run = false; + return flow.execute(function() { + flow.execute(throwStubError); + flow.execute(function() { run = true; }); + }).then(fail, function(e) { + assertIsStubError(e); + assertFalse(run); + }); +} + + +function testFrameCancelsRemainingTasks_onUnhandledPromiseRejection() { + var run = false; + return flow.execute(function() { + webdriver.promise.rejected(new StubError); + flow.execute(function() { run = true; }); + }).then(fail, function(e) { + assertIsStubError(e); + assertFalse(run); + }); +} + + +function testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_return() { + var seen = []; + return flow.execute(function() { + flow.execute(throwStubError); + + flow.execute(function() { + seen.push(1); + }).then(function() { + seen.push(2); + }, function() { + seen.push(3); + }); + }).then(fail, function(e) { + assertIsStubError(e); + assertArrayEquals([], seen); + }); +} + + +function testRegisteredTaskCallbacksAreDroppedWhenTaskIsCancelled_withReturn() { + var seen = []; + return flow.execute(function() { + flow.execute(throwStubError); + + return flow.execute(function() { + seen.push(1); + }).then(function() { + seen.push(2); + }, function() { + seen.push(3); + }); + }).then(fail, function(e) { + assertIsStubError(e); + assertArrayEquals([], seen); + }); +} + + +function testTasksWithinACallbackAreDroppedIfContainingTaskIsAborted() { + var seen = []; + return flow.execute(function() { + flow.execute(throwStubError); + + // None of the callbacks on this promise should execute because the + // task failure above is never handled, causing the containing task to + // abort. + webdriver.promise.fulfilled().then(function() { + seen.push(1); + return flow.execute(function() { + seen.push(2); + }); + }).thenFinally(function() { + seen.push(3); + }); + + }).then(fail, function(e) { + assertIsStubError(e); + assertArrayEquals([], seen); + }); +} + + +function testTaskIsCancelledAfterWaitTimeout() { + var seen = []; + return flow.execute(function() { + flow.wait(function() { + return webdriver.promise.delayed(50); + }, 5); + + return flow.execute(function() { + seen.push(1); + }).then(function() { + seen.push(2); + }, function() { + seen.push(3); + }); + }).then(fail, function() { + assertArrayEquals([], seen); + }); +} + + +function +testTaskCallbacksGetCancellationErrorIfRegisteredAfterTaskIsCancelled_1() { + var task; + flow.execute(function() { + flow.execute(throwStubError); + task = flow.execute(goog.nullFunction); + }).then(fail, assertIsStubError); + return waitForIdle().then(function() { + return task.then(fail, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + }); + }); +} + + +function +testTaskCallbacksGetCancellationErrorIfRegisteredAfterTaskIsCancelled_2() { + var seen = []; + + var task; + flow.execute(function() { + flow.execute(throwStubError); + task = flow.execute(goog.nullFunction); + + task.then(() => seen.push(1)) + .then(() => seen.push(2)); + task.then(() => seen.push(3)) + .then(() => seen.push(4)); + + }).then(fail, assertIsStubError); + + return waitForIdle().then(function() { + return task.then(fail, function(e) { + seen.push(5); + assertTrue(e instanceof webdriver.promise.CancellationError); + }); + }).then(() => assertArrayEquals([5], seen)); +} + + +function testUnhandledRejectionInParallelTaskQueue() { + var seen = []; + function schedule(name) { + return flow.execute(() => seen.push(name), name); + } + + flow.async(function() { + schedule('a.1'); + flow.execute(throwStubError, 'a.2 (throws)'); + }); + + var b3; + flow.async(function() { + schedule('b.1'); + schedule('b.2'); + b3 = schedule('b.3'); + }); + + var c3; + flow.async(function() { + schedule('c.1'); + schedule('c.2'); + c3 = schedule('c.3'); + }); + + function assertWasCancelled(p) { + return p.then(fail, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + }); + } + + return waitForAbort() + .then(function() { + assertArrayEquals(['a.1', 'b.1', 'c.1', 'b.2', 'c.2'], seen); + assertFalse(b3.isPending()); + assertFalse(c3.isPending()); + }) + .then(() => assertWasCancelled(b3)) + .then(() => assertWasCancelled(c3)); +} + + +function testErrorsInAsyncFunctionsAreReportedAsUnhandledRejection() { + flow.removeAllListeners(); // For tearDown. + + var task; + return new Promise(function(fulfill) { + flow.once('uncaughtException', fulfill); + flow.async(function() { + task = flow.execute(function() {}); + throw Error('boom'); + }); + }).then(function(error) { + assertTrue(error instanceof webdriver.promise.CancellationError); + assertFalse(task.isPending()); + return task.thenCatch(function(error) { + assertTrue(error instanceof webdriver.promise.CancellationError); + }); + }); +} + + +function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_1() { + var p1 = webdriver.promise.fulfilled(); + var reason = webdriver.promise.fulfilled('should not see me'); + return p1.then(function() { + throw reason; + }).then(fail, function(e) { + assertEquals(reason, e); + }); +} + + +function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_2() { + var p1 = webdriver.promise.fulfilled(); + var reason = webdriver.promise.rejected('should not see me'); + reason.thenCatch(goog.nullFunction); // For tearDown. + return p1.then(function() { + throw reason; + }).then(fail, function(e) { + assertEquals(reason, e); + }); +} + + +function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_3() { + var p1 = webdriver.promise.fulfilled(); + var reason = webdriver.promise.defer(); + setTimeout(() => reason.fulfill('should not see me'), 100); + return p1.then(function() { + throw reason.promise; + }).then(fail, function(e) { + assertEquals(reason.promise, e); + }); +} + + +function testDoesNotWaitForValuesThrownFromCallbacksToBeResolved_4() { + var p1 = webdriver.promise.fulfilled(); + var reason = {then: function() {}}; // A thenable like object. + return p1.then(function() { + throw reason; + }).then(fail, function(e) { + assertEquals(reason, e); + }); +} diff --git a/lib/webdriver/test/promise_flow_test.js b/lib/webdriver/test/promise_flow_test.js new file mode 100644 index 0000000..cdf39d4 --- /dev/null +++ b/lib/webdriver/test/promise_flow_test.js @@ -0,0 +1,2314 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.array'); +goog.require('goog.string'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.promise'); +goog.require('webdriver.stacktrace.Snapshot'); +goog.require('webdriver.stacktrace'); +goog.require('webdriver.test.testutil'); + +// Aliases for readability. +var StubError = webdriver.test.testutil.StubError, + throwStubError = webdriver.test.testutil.throwStubError, + assertIsStubError = webdriver.test.testutil.assertIsStubError, + assertingMessages = webdriver.test.testutil.assertingMessages, + callbackHelper = webdriver.test.testutil.callbackHelper, + callbackPair = webdriver.test.testutil.callbackPair; + +var flow, flowHistory, uncaughtExceptions; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + +function setUp() { + webdriver.promise.LONG_STACK_TRACES = false; + flow = new webdriver.promise.ControlFlow(); + webdriver.promise.setDefaultFlow(flow); + webdriver.test.testutil.messages = []; + flowHistory = []; + + uncaughtExceptions = []; + flow.on(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + onUncaughtException); +} + + +function tearDown() { + flow.removeAllListeners( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + assertArrayEquals('There were uncaught exceptions', [], uncaughtExceptions); + flow.reset(); + webdriver.promise.LONG_STACK_TRACES = false; +} + + +function onUncaughtException(e) { + uncaughtExceptions.push(e); +} + + +function waitForAbort(opt_flow) { + var theFlow = opt_flow || flow; + theFlow.removeAllListeners( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new goog.Promise(function(fulfill, reject) { + theFlow.once(webdriver.promise.ControlFlow.EventType.IDLE, function() { + reject(Error('expected flow to report an unhandled error')); + }); + theFlow.once( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + fulfill); + }); +} + + +function waitForIdle(opt_flow) { + var theFlow = opt_flow || flow; + return new goog.Promise(function(fulfill, reject) { + theFlow.once(webdriver.promise.ControlFlow.EventType.IDLE, fulfill); + theFlow.once( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, reject); + }); +} + +function timeout(ms) { + return new goog.Promise(function(fulfill) { + setTimeout(fulfill, ms); + }); +} + + +function schedule(msg, opt_return) { + return scheduleAction(msg, function() { + return opt_return; + }); +} + +/** + * @param {string} value The value to push. + * @param {webdriver.promise.Promise=} opt_taskPromise Promise to return from + * the task. + * @return {!webdriver.promise.Promise} The result. + */ +function schedulePush(value, opt_taskPromise) { + return scheduleAction(value, function() { + webdriver.test.testutil.messages.push(value); + return opt_taskPromise; + }); +} + +/** + * @param {string} msg Debug message. + * @param {!Function} actionFn The function. + * @return {!webdriver.promise.Promise} The function result. + */ +function scheduleAction(msg, actionFn) { + return webdriver.promise.controlFlow().execute(function() { + flowHistory.push(msg); + return actionFn(); + }, msg); +} + +/** + * @param {!Function} condition The condition function. + * @param {number=} opt_timeout The timeout. + * @param {string=} opt_message Optional message. + * @return {!webdriver.promise.Promise} The wait result. + */ +function scheduleWait(condition, opt_timeout, opt_message) { + var msg = opt_message || ''; + // It's not possible to hook into when the wait itself is scheduled, so + // we record each iteration of the wait loop. + var count = 0; + return webdriver.promise.controlFlow().wait(function() { + flowHistory.push((count++) + ': ' + msg); + return condition(); + }, opt_timeout, msg); +} + + +function assertFlowHistory(var_args) { + var expected = goog.array.slice(arguments, 0); + assertArrayEquals(expected, flowHistory); +} + + +function testScheduling_aSimpleFunction() { + schedule('go'); + return waitForIdle().then(function() { + assertFlowHistory('go'); + }); +} + + +function testScheduling_aSimpleFunctionWithANonPromiseReturnValue() { + schedule('go', 123).then(function(value) { + assertEquals(123, value); + }); + return waitForIdle().then(function() { + assertFlowHistory('go'); + }); +} + + +function testScheduling_aSimpleSequence() { + schedule('a'); + schedule('b'); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testScheduling_invokesCallbacksWhenTaskIsDone() { + var d = new webdriver.promise.Deferred(); + var called = false; + var done = schedule('a', d.promise).then(function(value) { + called = true; + assertEquals(123, value); + }); + return timeout(5).then(function() { + assertFalse(called); + d.fulfill(123); + return done; + }). + then(waitForIdle). + then(function() { + assertFlowHistory('a'); + }); +} + + +function testScheduling_blocksUntilPromiseReturnedByTaskIsResolved() { + var done = webdriver.promise.defer(); + schedulePush('a', done.promise); + schedulePush('b'); + setTimeout(function() { + done.fulfill(); + webdriver.test.testutil.messages.push('c'); + }, 25); + return waitForIdle().then(assertingMessages('a', 'c', 'b')); +} + + +function testScheduling_waitsForReturnedPromisesToResolve() { + var d1 = new webdriver.promise.Deferred(); + var d2 = new webdriver.promise.Deferred(); + + var callback; + schedule('a', d1.promise).then(callback = callbackHelper(function(value) { + assertEquals('fluffy bunny', value); + })); + + return timeout(5).then(function() { + callback.assertNotCalled('d1 not resolved yet'); + d1.fulfill(d2); + return timeout(5); + }).then(function() { + callback.assertNotCalled('d2 not resolved yet'); + d2.fulfill('fluffy bunny'); + return waitForIdle(); + }).then(function() { + callback.assertCalled('d2 has been resolved'); + assertFlowHistory('a'); + }); +} + + +function testScheduling_executesTasksInAFutureTurnAfterTheyAreScheduled() { + var count = 0; + function incr() { count++; } + + scheduleAction('', incr); + assertEquals(0, count); + return waitForIdle().then(function() { + assertEquals(1, count); + }); +} + + +function testScheduling_executesOneTaskPerTurnOfTheEventLoop() { + var order = []; + function go() { + order.push(order.length / 2); + goog.async.run(function() { + order.push('-'); + }); + } + + scheduleAction('', go); + scheduleAction('', go); + return waitForIdle().then(function() { + assertArrayEquals([0, '-', 1, '-'], order); + }) +} + + +function testScheduling_firstScheduledTaskIsWithinACallback() { + webdriver.promise.fulfilled().then(function() { + schedule('a'); + schedule('b'); + schedule('c'); + }).then(function() { + assertFlowHistory('a', 'b', 'c'); + }); + return waitForIdle(); +} + + +function testScheduling_newTasksAddedWhileWaitingOnTaskReturnedPromise1() { + scheduleAction('a', function() { + var d = webdriver.promise.defer(); + setTimeout(function() { + schedule('c'); + d.fulfill(); + }, 10); + return d.promise; + }); + schedule('b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testScheduling_newTasksAddedWhileWaitingOnTaskReturnedPromise2() { + scheduleAction('a', function() { + var d = webdriver.promise.defer(); + setTimeout(function() { + schedule('c'); + goog.async.run(d.fulfill); + }, 10); + return d.promise; + }); + schedule('b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); +} + + +function testFraming_callbacksRunInANewFrame() { + schedule('a').then(function() { + schedule('c'); + }); + schedule('b'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); +} + + +function testFraming_lotsOfNesting() { + schedule('a').then(function() { + schedule('c').then(function() { + schedule('e').then(function() { + schedule('g'); + }); + schedule('f'); + }); + schedule('d'); + }); + schedule('b'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'e', 'g', 'f', 'd', 'b'); + }); +} + + +function testFrame_callbackReturnsPromiseThatDependsOnATask_1() { + schedule('a').then(function() { + schedule('b'); + return webdriver.promise.delayed(5).then(function() { + return schedule('c'); + }); + }); + schedule('d'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); +} + + +function testFrame_callbackReturnsPromiseThatDependsOnATask_2() { + schedule('a').then(function() { + schedule('b'); + return webdriver.promise.delayed(5). + then(function() { return webdriver.promise.delayed(5) }). + then(function() { return webdriver.promise.delayed(5) }). + then(function() { return webdriver.promise.delayed(5) }). + then(function() { return schedule('c'); }); + }); + schedule('d'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); +} + + +function testFraming_eachCallbackWaitsForAllScheduledTasksToComplete() { + schedule('a'). + then(function() { + schedule('b'); + schedule('c'); + }). + then(function() { + schedule('d'); + }); + schedule('e'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); +} + + +function testFraming_eachCallbackWaitsForReturnTasksToComplete() { + schedule('a'). + then(function() { + schedule('b'); + return schedule('c'); + }). + then(function() { + schedule('d'); + }); + schedule('e'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); +} + + +function testFraming_callbacksOnAResolvedPromiseInsertIntoTheCurrentFlow() { + webdriver.promise.fulfilled().then(function() { + schedule('b'); + }); + schedule('a'); + + return waitForIdle().then(function() { + assertFlowHistory('b', 'a'); + }); +} + + +function testFraming_callbacksInterruptTheFlowWhenPromiseIsResolved() { + schedule('a').then(function() { + schedule('c'); + }); + schedule('b'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); +} + + +function testFraming_allCallbacksInAFrameAreScheduledWhenPromiseIsResolved() { + var a = schedule('a'); + a.then(function() { schedule('b'); }); + schedule('c'); + a.then(function() { schedule('d'); }); + schedule('e'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'd', 'c', 'e'); + }); +} + + +function testFraming_tasksScheduledInInActiveFrameDoNotGetPrecedence() { + var d = webdriver.promise.fulfilled(); + schedule('a'); + schedule('b'); + d.then(function() { schedule('c'); }); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testFraming_tasksScheduledInAFrameGetPrecedence_1() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { + schedule('c'); + schedule('d'); + }); + var e = schedule('e'); + a.then(function() { + schedule('f'); + e.then(function() { + schedule('g'); + }); + schedule('h'); + }); + schedule('i'); + }); + schedule('j'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'); + }); +} + + +function testErrorHandling_thrownErrorsArePassedToTaskErrback() { + scheduleAction('function that throws', throwStubError). + then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testErrorHandling_thrownErrorsPropagateThroughPromiseChain() { + scheduleAction('function that throws', throwStubError). + then(fail). + then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testErrorHandling_catchesErrorsFromFailedTasksInAFrame() { + schedule('a').then(function() { + schedule('b'); + scheduleAction('function that throws', throwStubError); + }). + then(fail, assertIsStubError); + return waitForIdle(); +} + + +function testErrorHandling_abortsIfOnlyTaskReturnsAnUnhandledRejection() { + scheduleAction('function that returns rejected promise', function() { + return webdriver.promise.rejected(new StubError); + }); + return waitForAbort().then(assertIsStubError); +} + + +function testErrorHandling_abortsIfThereIsAnUnhandledRejection() { + webdriver.promise.rejected(new StubError); + schedule('this should not run'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory(/* none */); + }); +} + + +function testErrorHandling_abortsSequenceIfATaskFails() { + schedule('a'); + schedule('b'); + scheduleAction('c', throwStubError); + schedule('d'); // Should never execute. + + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testErrorHandling_abortsFromUnhandledFramedTaskFailures_1() { + schedule('outer task').then(function() { + scheduleAction('inner task', throwStubError); + }); + schedule('this should not run'); + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('outer task', 'inner task'); + }); +} + + +function testErrorHandling_abortsFromUnhandledFramedTaskFailures_2() { + schedule('a').then(function() { + schedule('b').then(function() { + scheduleAction('c', throwStubError); + // This should not execute. + schedule('d'); + }); + }); + + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testErrorHandling_abortsWhenErrorBubblesUpFromFullyResolvingAnObject() { + var callback = callbackHelper(function() { + return webdriver.promise.rejected('rejected 2'); + }); + + scheduleAction('', function() { + var obj = {'foo': webdriver.promise.rejected(new StubError)}; + return webdriver.promise.fullyResolved(obj).then(callback); + }); + + return waitForAbort(). + then(assertIsStubError). + then(callback.assertNotCalled); +} + + +function testErrorHandling_abortsWhenErrorBubblesUpFromFullyResolvingAnObject_withCallback() { + var callback1 = callbackHelper(function() { + return webdriver.promise.rejected('rejected 2'); + }); + var callback2 = callbackHelper(); + + scheduleAction('', function() { + var obj = {'foo': webdriver.promise.rejected(new StubError)}; + return webdriver.promise.fullyResolved(obj).then(callback1); + }).then(callback2); + + return waitForAbort(). + then(assertIsStubError). + then(callback1.assertNotCalled). + then(callback2.assertNotCalled); +} + + +function testErrorHandling_canCatchErrorsFromNestedTasks() { + var errback; + schedule('a'). + then(function() { + return scheduleAction('b', throwStubError); + }). + thenCatch(errback = callbackHelper(assertIsStubError)); + return waitForIdle().then(errback.assertCalled); +} + + +function testErrorHandling_nestedCommandFailuresCanBeCaughtAndSuppressed() { + var errback; + schedule('a').then(function() { + return schedule('b').then(function() { + return schedule('c').then(function() { + throw new StubError; + }); + }); + }).thenCatch(errback = callbackHelper(assertIsStubError)); + schedule('d'); + return waitForIdle(). + then(errback.assertCalled). + then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); +} + + +function testErrorHandling_aTaskWithAnUnhandledPromiseRejection() { + schedule('a'); + scheduleAction('sub-tasks', function() { + webdriver.promise.rejected(new StubError); + }); + schedule('should never run'); + + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); +} + +function testErrorHandling_aTaskThatReutrnsARejectedPromise() { + schedule('a'); + scheduleAction('sub-tasks', function() { + return webdriver.promise.rejected(new StubError); + }); + schedule('should never run'); + + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); +} + + +function testErrorHandling_discardsSubtasksIfTaskThrows() { + var pair = callbackPair(null, assertIsStubError); + scheduleAction('a', function() { + schedule('b'); + schedule('c'); + throwStubError(); + }).then(pair.callback, pair.errback); + schedule('d'); + + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory('a', 'd'); + }); +} + + +function testErrorHandling_discardsRemainingSubtasksIfASubtaskFails() { + var pair = callbackPair(null, assertIsStubError); + scheduleAction('a', function() { + schedule('b'); + scheduleAction('c', throwStubError); + schedule('d'); + }).then(pair.callback, pair.errback); + schedule('e'); + + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory('a', 'b', 'c', 'e'); + }); +} + + +function testTryFinally_happyPath() { + /* Model: + try { + doFoo(); + doBar(); + } finally { + doBaz(); + } + */ + schedulePush('foo'). + then(goog.partial(schedulePush, 'bar')). + thenFinally(goog.partial(schedulePush, 'baz')); + return waitForIdle().then(assertingMessages('foo', 'bar', 'baz')); +} + + +function testTryFinally_firstTryFails() { + /* Model: + try { + doFoo(); + doBar(); + } finally { + doBaz(); + } + */ + + scheduleAction('doFoo and throw', function() { + webdriver.test.testutil.messages.push('foo'); + throw new StubError; + }). + then(function() { schedulePush('bar'); }). + thenFinally(function() { schedulePush('baz'); }); + + return waitForAbort(). + then(assertIsStubError). + then(assertingMessages('foo', 'baz')); +} + + +function testTryFinally_secondTryFails() { + /* Model: + try { + doFoo(); + doBar(); + } finally { + doBaz(); + } + */ + + schedulePush('foo'). + then(function() { + return scheduleAction('doBar and throw', function() { + webdriver.test.testutil.messages.push('bar'); + throw new StubError; + }); + }). + thenFinally(function() { + return schedulePush('baz'); + }); + return waitForAbort(). + then(assertIsStubError). + then(assertingMessages('foo', 'bar', 'baz')); +} + + +function testTaskCallbacksInterruptFlow() { + schedule('a').then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function +testTaskCallbacksInterruptFlow_taskDependsOnImmediatelyFulfilledPromise() { + scheduleAction('a', function() { + return webdriver.promise.fulfilled(); + }).then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testTaskCallbacksInterruptFlow_taskDependsOnPreviouslyFulfilledPromise() { + var promise = webdriver.promise.fulfilled(123); + scheduleAction('a', function() { + return promise; + }).then(function(value) { + assertEquals(123, value); + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testTaskCallbacksInterruptFlow_taskDependsOnAsyncPromise() { + scheduleAction('a', function() { + return webdriver.promise.delayed(25); + }).then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testPromiseChainedToTaskInterruptFlow() { + schedule('a').then(function() { + return webdriver.promise.fulfilled(); + }).then(function() { + return webdriver.promise.fulfilled(); + }).then(function() { + schedule('b'); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testNestedTaskCallbacksInterruptFlowWhenResolved() { + schedule('a').then(function() { + schedule('b').then(function() { + schedule('c'); + }); + }); + schedule('d'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd'); + }); +} + + +function testDelayedNesting_1() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }); + schedule('d'); + }); + schedule('e'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); +} + + +function testDelayedNesting_2() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }); + schedule('d'); + a.then(function() { schedule('e'); }); + }); + schedule('f'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + }); +} + + +function testDelayedNesting_3() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }); + a.then(function() { schedule('d'); }); + }); + schedule('e'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e'); + }); +} + + +function testDelayedNesting_4() { + var a = schedule('a'); + schedule('b').then(function() { + a.then(function() { schedule('c'); }).then(function() { + schedule('d'); + }); + a.then(function() { schedule('e'); }); + }); + schedule('f'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f'); + }); +} + + +function testDelayedNesting_5() { + var a = schedule('a'); + schedule('b').then(function() { + var c; + a.then(function() { c = schedule('c'); }).then(function() { + schedule('d'); + a.then(function() { schedule('e'); }); + c.then(function() { schedule('f'); }); + schedule('g'); + }); + a.then(function() { schedule('h'); }); + }); + schedule('i'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'); + }); +} + + +function testWaiting_onAConditionThatIsAlwaysTrue() { + scheduleWait(function() { return true;}, 0, 'waiting on true'); + return waitForIdle().then(function() { + assertFlowHistory('0: waiting on true'); + }); +} + + +function testWaiting_aSimpleCountingCondition() { + var count = 0; + scheduleWait(function() { + return ++count == 3; + }, 100, 'counting to 3'); + + return waitForIdle().then(function() { + assertEquals(3, count); + }); +} + + +function testWaiting_aConditionThatReturnsAPromise() { + var d = new webdriver.promise.Deferred(); + var count = 0; + + scheduleWait(function() { + count += 1; + return d.promise; + }, 0, 'waiting for promise'); + + return timeout(50).then(function() { + assertEquals(1, count); + d.fulfill(123); + return waitForIdle(); + }); +} + + +function testWaiting_aConditionThatReturnsAPromise_2() { + var count = 0; + scheduleWait(function() { + return webdriver.promise.fulfilled(++count == 3); + }, 100, 'waiting for promise'); + + return waitForIdle().then(function() { + assertEquals(3, count); + }); +} + + +function testWaiting_aConditionThatReturnsATaskResult() { + var count = 0; + scheduleWait(function() { + return scheduleAction('increment count', function() { + return ++count == 3; + }); + }, 100, 'counting to 3'); + schedule('post wait'); + + return waitForIdle().then(function() { + assertEquals(3, count); + assertFlowHistory( + '0: counting to 3', 'increment count', + '1: counting to 3', 'increment count', + '2: counting to 3', 'increment count', + 'post wait'); + }); +} + + +function testWaiting_conditionContainsASubtask() { + var count = 0; + scheduleWait(function() { + schedule('sub task'); + return ++count == 3; + }, 100, 'counting to 3'); + schedule('post wait'); + + return waitForIdle().then(function() { + assertEquals(3, count); + assertFlowHistory( + '0: counting to 3', 'sub task', + '1: counting to 3', 'sub task', + '2: counting to 3', 'sub task', + 'post wait'); + }); +} + + +function testWaiting_cancelsWaitIfScheduledTaskFails() { + var pair = callbackPair(null, assertIsStubError); + scheduleWait(function() { + scheduleAction('boom', throwStubError); + schedule('this should not run'); + return true; + }, 100, 'waiting to go boom').then(pair.callback, pair.errback); + schedule('post wait'); + + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFlowHistory( + '0: waiting to go boom', 'boom', + 'post wait'); + }); +} + + +function testWaiting_failsIfConditionThrows() { + var callbacks = callbackPair(null, assertIsStubError); + scheduleWait(throwStubError, 0, 'goes boom'). + then(callbacks.callback, callbacks.errback); + schedule('post wait'); + + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); +} + + +function testWaiting_failsIfConditionReturnsARejectedPromise() { + var callbacks = callbackPair(null, assertIsStubError); + scheduleWait(function() { + return webdriver.promise.rejected(new StubError); + }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); + schedule('post wait'); + + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); +} + + +function testWaiting_failsIfConditionHasUnhandledRejection() { + var callbacks = callbackPair(null, assertIsStubError); + scheduleWait(function() { + webdriver.promise.controlFlow().execute(throwStubError); + }, 0, 'goes boom').then(callbacks.callback, callbacks.errback); + schedule('post wait'); + + return waitForIdle(). + then(callbacks.assertErrback). + then(function() { + assertFlowHistory('0: goes boom', 'post wait'); + }); +} + + +function testWaiting_failsIfConditionHasAFailedSubtask() { + var callbacks = callbackPair(null, assertIsStubError); + var count = 0; + scheduleWait(function() { + scheduleAction('maybe throw', function() { + if (++count == 2) { + throw new StubError; + } + }); + }, 100, 'waiting').then(callbacks.callback, callbacks.errback); + schedule('post wait'); + + return waitForIdle().then(function() { + assertEquals(2, count); + assertFlowHistory( + '0: waiting', 'maybe throw', + '1: waiting', 'maybe throw', + 'post wait'); + }); +} + + +function testWaiting_pollingLoopWaitsForAllScheduledTasksInCondition() { + var count = 0; + scheduleWait(function() { + scheduleAction('increment count', function() { ++count; }); + return count >= 3; + }, 100, 'counting to 3'); + schedule('post wait'); + + return waitForIdle().then(function() { + assertEquals(4, count); + assertFlowHistory( + '0: counting to 3', 'increment count', + '1: counting to 3', 'increment count', + '2: counting to 3', 'increment count', + '3: counting to 3', 'increment count', + 'post wait'); + }); +} + + +function testWaiting_waitsForeverOnAZeroTimeout() { + var done = false; + setTimeout(function() { + done = true; + }, 150); + var waitResult = scheduleWait(function() { + return done; + }, 0); + + return timeout(75).then(function() { + assertFalse(done); + return timeout(100); + }).then(function() { + assertTrue(done); + return waitResult; + }); +} + + +function testWaiting_waitsForeverIfTimeoutOmitted() { + var done = false; + setTimeout(function() { + done = true; + }, 150); + var waitResult = scheduleWait(function() { + return done; + }); + + return timeout(75).then(function() { + assertFalse(done); + return timeout(100); + }).then(function() { + assertTrue(done); + return waitResult; + }); +} + + +function testWaiting_timesOut_nonZeroTimeout() { + var count = 0; + scheduleWait(function() { + count += 1; + var ms = count === 2 ? 65 : 5; + return webdriver.promise.delayed(ms).then(function() { + return false; + }); + }, 60, 'counting to 3'); + return waitForAbort().then(function(e) { + switch (count) { + case 1: + assertFlowHistory('0: counting to 3'); + break; + case 2: + assertFlowHistory('0: counting to 3', '1: counting to 3'); + break; + default: + fail('unexpected polling count: ' + count); + } + assertRegExp(/^counting to 3\nWait timed out after \d+ms$/, e.message); + }); +} + + +function testWaiting_shouldFailIfConditionReturnsARejectedPromise() { + scheduleWait(function() { + return webdriver.promise.rejected(new StubError); + }, 100, 'returns rejected promise on first pass'); + return waitForAbort().then(assertIsStubError); +} + + +function testWaiting_scheduleWithIntermittentWaits() { + schedule('a'); + scheduleWait(function() { return true; }, 0, 'wait 1'); + schedule('b'); + scheduleWait(function() { return true; }, 0, 'wait 2'); + schedule('c'); + scheduleWait(function() { return true; }, 0, 'wait 3'); + + return waitForIdle().then(function() { + assertFlowHistory('a', '0: wait 1', 'b', '0: wait 2', 'c', '0: wait 3'); + }); +} + + +function testWaiting_scheduleWithIntermittentAndNestedWaits() { + schedule('a'); + scheduleWait(function() { return true; }, 0, 'wait 1'). + then(function() { + schedule('d'); + scheduleWait(function() { return true; }, 0, 'wait 2'); + schedule('e'); + }); + schedule('b'); + scheduleWait(function() { return true; }, 0, 'wait 3'); + schedule('c'); + scheduleWait(function() { return true; }, 0, 'wait 4'); + + return waitForIdle().then(function() { + assertFlowHistory( + 'a', '0: wait 1', 'd', '0: wait 2', 'e', 'b', '0: wait 3', 'c', + '0: wait 4'); + }); +} + + +function testWait_requiresConditionToBeAPromiseOrFunction() { + assertThrows(function() { + flow.wait(1234, 0); + }); + flow.wait(function() { return true;}, 0); + flow.wait(webdriver.promise.fulfilled(), 0); + return waitForIdle(); +} + + +function testWait_promiseThatDoesNotResolveBeforeTimeout() { + var d = webdriver.promise.defer(); + flow.wait(d.promise, 5).then(fail, function(e) { + assertRegExp(/Timed out waiting for promise to resolve after \d+ms/, + e.message); + }); + return waitForIdle().then(function() { + assertTrue('Promise should not be cancelled', d.promise.isPending()); + }); +} + + +function testWait_unboundedWaitOnPromiseResolution() { + var messages = []; + var d = webdriver.promise.defer(); + var waitResult = flow.wait(d.promise).then(function(value) { + messages.push('b'); + assertEquals(1234, value); + }); + setTimeout(function() { + messages.push('a'); + }, 5); + + webdriver.promise.delayed(10).then(function() { + assertArrayEquals(['a'], messages); + assertTrue(waitResult.isPending()); + d.fulfill(1234); + return waitResult; + }).then(function() { + assertArrayEquals(['a', 'b'], messages); + }); + + return waitForIdle(); +} + + +function testSubtasks() { + schedule('a'); + scheduleAction('sub-tasks', function() { + schedule('c'); + schedule('d'); + }); + schedule('b'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks', 'c', 'd', 'b'); + }); +} + + +function testSubtasks_nesting() { + schedule('a'); + scheduleAction('sub-tasks', function() { + schedule('b'); + scheduleAction('sub-sub-tasks', function() { + schedule('c'); + schedule('d'); + }); + schedule('e'); + }); + schedule('f'); + + return waitForIdle().then(function() { + assertFlowHistory( + 'a', 'sub-tasks', 'b', 'sub-sub-tasks', 'c', 'd', 'e', 'f'); + }); +} + + +function testSubtasks_taskReturnsSubTaskResult_1() { + schedule('a'); + scheduleAction('sub-tasks', function() { + return schedule('c'); + }); + schedule('b'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks', 'c', 'b'); + }); +} + + +function testSubtasks_taskReturnsSubTaskResult_2() { + var callback; + schedule('a'); + schedule('sub-tasks', webdriver.promise.fulfilled(123)). + then(callback = callbackHelper(function(value) { + assertEquals(123, value); + })); + schedule('b'); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'sub-tasks','b'); + callback.assertCalled(); + }); +} + + +function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_1() { + scheduleAction('a', function() { + return webdriver.promise.delayed(10).then(function() { + schedule('b'); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_2() { + scheduleAction('a', function() { + return webdriver.promise.fulfilled().then(function() { + schedule('b'); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_3() { + scheduleAction('a', function() { + return webdriver.promise.delayed(10).then(function() { + return schedule('b'); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_4() { + scheduleAction('a', function() { + return webdriver.promise.delayed(5).then(function() { + return webdriver.promise.delayed(5).then(function() { + return schedule('b'); + }); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_5() { + scheduleAction('a', function() { + return webdriver.promise.delayed(5).then(function() { + return webdriver.promise.delayed(5).then(function() { + return webdriver.promise.delayed(5).then(function() { + return webdriver.promise.delayed(5).then(function() { + return schedule('b'); + }); + }); + }); + }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + + +function testSubtasks_taskReturnsPromiseThatDependsOnSubtask_6() { + scheduleAction('a', function() { + return webdriver.promise.delayed(5). + then(function() { return webdriver.promise.delayed(5) }). + then(function() { return webdriver.promise.delayed(5) }). + then(function() { return webdriver.promise.delayed(5) }). + then(function() { return schedule('b'); }); + }); + schedule('c'); + return waitForIdle().then(function() { + assertFlowHistory('a', 'b', 'c'); + }); +} + +function testSubtasks_subTaskFails_1() { + schedule('a'); + scheduleAction('sub-tasks', function() { + scheduleAction('sub-task that fails', throwStubError); + }); + schedule('should never execute'); + + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks', 'sub-task that fails'); + }); +} + + +function testSubtasks_subTaskFails_2() { + schedule('a'); + scheduleAction('sub-tasks', function() { + return webdriver.promise.rejected(new StubError); + }); + schedule('should never execute'); + + return waitForAbort(). + then(assertIsStubError). + then(function() { + assertFlowHistory('a', 'sub-tasks'); + }); +} + + +function testSubtasks_subTaskFails_3() { + var callbacks = callbackPair(null, assertIsStubError); + + schedule('a'); + scheduleAction('sub-tasks', function() { + return webdriver.promise.rejected(new StubError); + }).then(callbacks.callback, callbacks.errback); + schedule('b'); + + return waitForIdle(). + then(function() { + assertFlowHistory('a', 'sub-tasks', 'b'); + callbacks.assertErrback(); + }); +} + + +function testEventLoopWaitsOnPendingPromiseRejections_oneRejection() { + var d = new webdriver.promise.Deferred; + scheduleAction('one', function() { + return d.promise; + }); + scheduleAction('two', goog.nullFunction); + + return timeout(50).then(function() { + assertFlowHistory('one'); + d.reject(new StubError); + return waitForAbort(); + }). + then(assertIsStubError). + then(function() { + assertFlowHistory('one'); + }); +} + + +function testEventLoopWaitsOnPendingPromiseRejections_multipleRejections() { + var once = Error('once'); + var twice = Error('twice'); + + scheduleAction('one', function() { + webdriver.promise.rejected(once); + webdriver.promise.rejected(twice); + }); + var twoResult = scheduleAction('two', goog.nullFunction); + + flow.removeAllListeners( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new goog.Promise(function(fulfill, reject) { + setTimeout(function() { + reject(Error('Should have reported the two errors by now')); + }, 50); + flow.on( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + fulfill); + }).then(function(e) { + assertTrue(e instanceof webdriver.promise.MultipleUnhandledRejectionError); + // TODO: switch to Array.from when we drop node 0.12.x + var errors = []; + for (var e2 of e.errors) { + errors.push(e2); + } + assertArrayEquals([once, twice], errors); + assertFlowHistory('one'); + assertFalse('Did not cancel the second task', twoResult.isPending()); + }); +} + +function testCancelsPromiseReturnedByCallbackIfFrameFails_promiseCallback() { + var chainPair = callbackPair(null, assertIsStubError); + var deferredPair = callbackPair(null, function(e) { + assertEquals('callback result should be cancelled', + 'CancellationError: StubError', + e.toString()); + }); + + var d = new webdriver.promise.Deferred(); + d.then(deferredPair.callback, deferredPair.errback); + + webdriver.promise.fulfilled(). + then(function() { + scheduleAction('boom', throwStubError); + schedule('this should not run'); + return d.promise; + }). + then(chainPair.callback, chainPair.errback); + + return waitForIdle().then(function() { + assertFlowHistory('boom'); + chainPair.assertErrback('chain errback not invoked'); + deferredPair.assertErrback('deferred errback not invoked'); + }); +} + +function testCancelsPromiseReturnedByCallbackIfFrameFails_taskCallback() { + var chainPair = callbackPair(null, assertIsStubError); + var deferredPair = callbackPair(null, function(e) { + assertEquals('callback result should be cancelled', + 'CancellationError: StubError', + e.toString()); + }); + + var d = new webdriver.promise.Deferred(); + d.then(deferredPair.callback, deferredPair.errback); + + schedule('a'). + then(function() { + scheduleAction('boom', throwStubError); + schedule('this should not run'); + return d.promise; + }). + then(chainPair.callback, chainPair.errback); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'boom'); + chainPair.assertErrback('chain errback not invoked'); + deferredPair.assertErrback('deferred errback not invoked'); + }); +} + +function testMaintainsOrderInCallbacksWhenATaskReturnsAPromise() { + schedule('__start__', webdriver.promise.fulfilled()). + then(function() { + webdriver.test.testutil.messages.push('a'); + schedulePush('b'); + webdriver.test.testutil.messages.push('c'); + }). + then(function() { + webdriver.test.testutil.messages.push('d'); + }); + schedulePush('e'); + + return waitForIdle().then(function() { + assertFlowHistory('__start__', 'b', 'e'); + webdriver.test.testutil.assertMessages('a', 'c', 'b', 'd', 'e'); + }); +} + + +function assertFlowIs(flow) { + assertEquals(flow, webdriver.promise.controlFlow()); +} + +function testOwningFlowIsActivatedForExecutingTasks() { + var defaultFlow = webdriver.promise.controlFlow(); + var order = []; + + webdriver.promise.createFlow(function(flow) { + assertFlowIs(flow); + order.push(0); + + defaultFlow.execute(function() { + assertFlowIs(defaultFlow); + order.push(1); + }); + }); + + return waitForIdle().then(function() { + assertFlowIs(defaultFlow); + assertArrayEquals([0, 1], order); + }); +} + +function testCreateFlowReturnsPromisePairedWithCreatedFlow() { + return new goog.Promise(function(fulfill, reject) { + var newFlow; + webdriver.promise.createFlow(function(flow) { + newFlow = flow; + assertFlowIs(newFlow); + }).then(function() { + assertFlowIs(newFlow); + waitForIdle(newFlow).then(fulfill, reject); + }); + }); +} + +function testDeferredFactoriesCreateForActiveFlow_defaultFlow() { + var e = Error(); + var defaultFlow = webdriver.promise.controlFlow(); + webdriver.promise.fulfilled().then(function() { + assertFlowIs(defaultFlow); + }); + webdriver.promise.rejected(e).then(null, function(err) { + assertEquals(e, err); + assertFlowIs(defaultFlow); + }); + webdriver.promise.defer().then(function() { + assertFlowIs(defaultFlow); + }); + + return waitForIdle(); +} + + +function testDeferredFactoriesCreateForActiveFlow_newFlow() { + var e = Error(); + var newFlow = new webdriver.promise.ControlFlow; + newFlow.execute(function() { + webdriver.promise.fulfilled().then(function() { + assertFlowIs(newFlow); + }); + + webdriver.promise.rejected(e).then(null, function(err) { + assertEquals(e, err); + assertFlowIs(newFlow); + }); + + webdriver.promise.defer().then(function() { + assertFlowIs(newFlow); + }); + }).then(function() { + assertFlowIs(newFlow); + }); + + return waitForIdle(newFlow); +} + +function testFlowsSynchronizeWithThemselvesNotEachOther() { + var defaultFlow = webdriver.promise.controlFlow(); + schedulePush('a', 'a'); + webdriver.promise.controlFlow().timeout(250); + schedulePush('b', 'b'); + + webdriver.promise.createFlow(function() { + schedulePush('c', 'c'); + schedulePush('d', 'd'); + }); + + return waitForIdle().then(function() { + webdriver.test.testutil.assertMessages('a', 'c', 'd', 'b'); + }); +} + +function testUnhandledErrorsAreReportedToTheOwningFlow() { + var error1 = Error('e1'); + var error2 = Error('e2'); + + var defaultFlow = webdriver.promise.controlFlow(); + defaultFlow.removeAllListeners('uncaughtException'); + + var flow1Error = goog.Promise.withResolver(); + flow1Error.promise.then(function(value) { + assertEquals(error2, value); + }); + + var flow2Error = goog.Promise.withResolver(); + flow2Error.promise.then(function(value) { + assertEquals(error1, value); + }); + + webdriver.promise.createFlow(function(flow) { + flow.once('uncaughtException', flow2Error.resolve); + webdriver.promise.rejected(error1); + + defaultFlow.once('uncaughtException', flow1Error.resolve); + defaultFlow.execute(function() { + webdriver.promise.rejected(error2); + }); + }); + + return goog.Promise.all([flow1Error.promise, flow2Error.promise]); +} + +function testCanSynchronizeFlowsByReturningPromiseFromOneToAnother() { + var flow1 = new webdriver.promise.ControlFlow; + var flow1Done = goog.Promise.withResolver(); + flow1.once('idle', flow1Done.resolve); + flow1.once('uncaughtException', flow1Done.reject); + + var flow2 = new webdriver.promise.ControlFlow; + var flow2Done = goog.Promise.withResolver(); + flow2.once('idle', flow2Done.resolve); + flow2.once('uncaughtException', flow2Done.reject); + + flow1.execute(function() { + schedulePush('a', 'a'); + return webdriver.promise.delayed(25); + }, 'start flow 1'); + + flow2.execute(function() { + schedulePush('b', 'b'); + schedulePush('c', 'c'); + flow2.execute(function() { + return flow1.execute(function() { + schedulePush('d', 'd'); + }, 'flow 1 task'); + }, 'inject flow1 result into flow2'); + schedulePush('e', 'e'); + }, 'start flow 2'); + + return goog.Promise.all([flow1Done.promise, flow2Done.promise]). + then(function() { + webdriver.test.testutil.assertMessages('a', 'b', 'c', 'd', 'e'); + }); +} + +function testFramesWaitToCompleteForPendingRejections() { + return new goog.Promise(function(fulfill, reject) { + + webdriver.promise.controlFlow().execute(function() { + webdriver.promise.rejected(new StubError); + }).then(fulfill, reject); + + }). + then(goog.partial(fail, 'expected to fail'), assertIsStubError). + then(waitForIdle); +} + +function testSynchronizeErrorsPropagateToOuterFlow() { + var outerFlow = new webdriver.promise.ControlFlow; + var innerFlow = new webdriver.promise.ControlFlow; + + var block = goog.Promise.withResolver(); + innerFlow.execute(function() { + return block.promise; + }, 'block inner flow'); + + outerFlow.execute(function() { + block.resolve(); + return innerFlow.execute(function() { + webdriver.promise.rejected(new StubError); + }, 'trigger unhandled rejection error'); + }, 'run test'); + + return goog.Promise.all([ + waitForIdle(innerFlow), + waitForAbort(outerFlow).then(assertIsStubError) + ]); +} + +function testFailsIfErrbackThrows() { + webdriver.promise.rejected('').then(null, throwStubError); + return waitForAbort().then(assertIsStubError); +} + +function testFailsIfCallbackReturnsRejectedPromise() { + webdriver.promise.fulfilled().then(function() { + return webdriver.promise.rejected(new StubError); + }); + return waitForAbort().then(assertIsStubError); +} + +function testAbortsFrameIfTaskFails() { + webdriver.promise.fulfilled().then(function() { + webdriver.promise.controlFlow().execute(throwStubError); + }); + return waitForAbort().then(assertIsStubError); +} + +function testAbortsFramePromisedChainedFromTaskIsNotHandled() { + webdriver.promise.fulfilled().then(function() { + webdriver.promise.controlFlow().execute(goog.nullFunction). + then(throwStubError); + }); + return waitForAbort().then(assertIsStubError); +} + +function testTrapsChainedUnhandledRejectionsWithinAFrame() { + var pair = callbackPair(null, assertIsStubError); + webdriver.promise.fulfilled().then(function() { + webdriver.promise.controlFlow().execute(goog.nullFunction). + then(throwStubError); + }).then(pair.callback, pair.errback); + + return waitForIdle().then(pair.assertErrback); +} + + +function testCancelsRemainingTasksIfFrameThrowsDuringScheduling() { + var task1, task2; + var pair = callbackPair(null, assertIsStubError); + var flow = webdriver.promise.controlFlow(); + flow.execute(function() { + task1 = flow.execute(goog.nullFunction); + task2 = flow.execute(goog.nullFunction); + throw new StubError; + }).then(pair.callback, pair.errback); + + return waitForIdle(). + then(pair.assertErrback). + then(function() { + assertFalse(task1.isPending()); + pair = callbackPair(); + return task1.then(pair.callback, pair.errback); + }). + then(function() { + pair.assertErrback(); + assertFalse(task2.isPending()); + pair = callbackPair(); + return task2.then(pair.callback, pair.errback); + }). + then(function() { + pair.assertErrback(); + }); +} + +function testCancelsRemainingTasksInFrameIfATaskFails() { + var task; + var pair = callbackPair(null, assertIsStubError); + var flow = webdriver.promise.controlFlow(); + flow.execute(function() { + flow.execute(throwStubError); + task = flow.execute(goog.nullFunction); + }).then(pair.callback, pair.errback); + + return waitForIdle().then(pair.assertErrback).then(function() { + assertFalse(task.isPending()); + pair = callbackPair(); + task.then(pair.callback, pair.errback); + }).then(function() { + pair.assertErrback(); + }); +} + +function testDoesNotModifyRejectionErrorIfPromiseNotInsideAFlow() { + var error = Error('original message'); + var originalStack = error.stack; + var originalStr = error.toString(); + + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + assertEquals('original message', e.message); + assertEquals(originalStack, e.stack); + assertEquals(originalStr, e.toString()); + }); + + webdriver.promise.rejected(error).then(pair.callback, pair.errback); + return waitForIdle().then(pair.assertErrback); +} + + +/** See https://github.com/SeleniumHQ/selenium/issues/444 */ +function testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_1() { + var messages = []; + flow.execute(function() { + return webdriver.promise.fulfilled(['a', 'b', 'c', 'd']); + }, 'start').then(function(steps) { + steps.forEach(function(step) { + webdriver.promise.fulfilled(step) + .then(function() { + messages.push(step + '.1'); + }).then(function() { + messages.push(step + '.2'); + }); + }) + }); + return waitForIdle().then(function() { + assertArrayEquals( + ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], + messages); + }); +} + + +/** See https://github.com/SeleniumHQ/selenium/issues/444 */ +function testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_2() { + var messages = []; + flow.execute(function() { + return webdriver.promise.fulfilled(['a', 'b', 'c', 'd']); + }, 'start').then(function(steps) { + steps.forEach(function(step) { + webdriver.promise.fulfilled(step) + .then(function() { + messages.push(step + '.1'); + }).then(function() { + flow.execute(function() {}, step + '.2').then(function() { + messages.push(step + '.2'); + }); + }); + }) + }); + return waitForIdle().then(function() { + assertArrayEquals( + ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], + messages); + }); +} + + +/** See https://github.com/SeleniumHQ/selenium/issues/444 */ +function testMaintainsOrderWithPromiseChainsCreatedWithinAForeach_3() { + var messages = []; + flow.execute(function() { + return webdriver.promise.fulfilled(['a', 'b', 'c', 'd']); + }, 'start').then(function(steps) { + steps.forEach(function(step) { + webdriver.promise.fulfilled(step) + .then(function(){}) + .then(function() { + messages.push(step + '.1'); + return flow.execute(function() {}, step + '.1'); + }).then(function() { + flow.execute(function() {}, step + '.2').then(function(text) { + messages.push(step + '.2'); + }); + }); + }) + }); + return waitForIdle().then(function() { + assertArrayEquals( + ['a.1', 'a.2', 'b.1', 'b.2', 'c.1', 'c.2', 'd.1', 'd.2'], + messages); + }); +} + +/** See https://github.com/SeleniumHQ/selenium/issues/363 */ +function +testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_1() { + scheduleAction('a', () => webdriver.promise.delayed(10)); + schedule('b'); + setTimeout(() => schedule('c'), 0); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'b'); + }); +} + + +/** See https://github.com/SeleniumHQ/selenium/issues/363 */ +function +testTasksScheduledInASeparateTurnOfTheEventLoopGetASeparateTaskQueue_2() { + scheduleAction('a', () => webdriver.promise.delayed(10)); + schedule('b'); + schedule('c'); + setTimeout(function() { + schedule('d'); + scheduleAction('e', () => webdriver.promise.delayed(10)); + schedule('f'); + }, 0); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'd', 'e', 'b', 'c', 'f'); + }); +} + + +/** See https://github.com/SeleniumHQ/selenium/issues/363 */ +function testCanSynchronizeTasksFromAdjacentTaskQueues() { + var task1 = scheduleAction('a', () => webdriver.promise.delayed(10)); + schedule('b'); + setTimeout(function() { + scheduleAction('c', () => task1); + schedule('d'); + }, 0); + + return waitForIdle().then(function() { + assertFlowHistory('a', 'c', 'd', 'b'); + }); +} + + +function testCancellingAScheduledTask_1() { + var called = false; + var task1 = scheduleAction('a', () => called = true); + task1.cancel('no soup for you'); + + return waitForIdle().then(function() { + assertFalse(called); + assertFlowHistory(); + return task1.thenCatch(function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('no soup for you', e.message); + }); + }); +} + + +function testCancellingAScheduledTask_2() { + schedule('a'); + var called = false; + var task2 = scheduleAction('b', () => called = true); + schedule('c'); + + task2.cancel('no soup for you'); + + return waitForIdle().then(function() { + assertFalse(called); + assertFlowHistory('a', 'c'); + return task2.thenCatch(function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('no soup for you', e.message); + }); + }); +} + + +function testCancellingAScheduledTask_3() { + var called = false; + var task = scheduleAction('a', () => called = true); + task.cancel(new StubError); + + return waitForIdle().then(function() { + assertFalse(called); + assertFlowHistory(); + return task.thenCatch(function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + }); + }); +} + + +function testCancellingAScheduledTask_4() { + var seen = []; + var task = scheduleAction('a', () => seen.push(1)) + .then(() => seen.push(2)) + .then(() => seen.push(3)) + .then(() => seen.push(4)) + .then(() => seen.push(5)); + task.cancel(new StubError); + + return waitForIdle().then(function() { + assertArrayEquals([], seen); + assertFlowHistory(); + return task.thenCatch(function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + }); + }); +} + + +function testCancellingAScheduledTask_fromWithinAnExecutingTask() { + var called = false; + var task; + scheduleAction('a', function() { + task.cancel('no soup for you'); + }); + task = scheduleAction('b', () => called = true); + schedule('c'); + + return waitForIdle().then(function() { + assertFalse(called); + assertFlowHistory('a', 'c'); + return task.thenCatch(function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('no soup for you', e.message); + }); + }); +} + + +function testCancellingAPendingTask() { + var order = []; + var unresolved = webdriver.promise.defer(); + + var innerTask; + var outerTask = scheduleAction('a', function() { + order.push(1); + + // Schedule a task that will never finish. + innerTask = scheduleAction('a.1', function() { + return unresolved.promise; + }); + + innerTask.thenCatch(function(e) { + order.push(2); + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('no soup for you', e.message); + }); + }); + schedule('b'); + + outerTask.thenCatch(function(e) { + order.push(3); + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('no soup for you', e.message); + }); + + unresolved.promise.thenCatch(function(e) { + order.push(4); + }); + + return timeout(10).then(function() { + assertArrayEquals([1], order); + assertTrue(unresolved.promise.isPending()); + + outerTask.cancel('no soup for you'); + return waitForIdle(); + }).then(function() { + assertFlowHistory('a', 'a.1', 'b'); + assertArrayEquals([1, 4, 2, 3], order); + }); +} + + +function testCancellingAPendingPromiseCallback() { + var called = false; + + var root = webdriver.promise.fulfilled(); + root.then(function() { + cb2.cancel('no soup for you'); + }); + + var cb2 = root.then(fail, fail); // These callbacks should never be called. + cb2.then(fail, function(e) { + called = true; + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('no soup for you', e.message); + }); + + return waitForIdle().then(function() { + assertTrue(called); + }); +} + + +function testResetFlow_1() { + var called = 0; + var task = flow.execute(() => called++); + task.thenFinally(() => called++); + + return new Promise(function(fulfill) { + flow.once('reset', fulfill); + flow.reset(); + + }).then(function() { + assertEquals(0, called); + assertFalse(task.isPending()); + return task; + + }).then(fail, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('ControlFlow was reset', e.message); + }); +} + + +function testResetFlow_2() { + var called = 0; + var task1 = flow.execute(() => called++); + task1.thenFinally(() => called++); + + var task2 = flow.execute(() => called++); + task2.thenFinally(() => called++); + + var task3 = flow.execute(() => called++); + task3.thenFinally(() => called++); + + return new Promise(function(fulfill) { + flow.once('reset', fulfill); + flow.reset(); + + }).then(function() { + assertEquals(0, called); + assertFalse(task1.isPending()); + assertFalse(task2.isPending()); + assertFalse(task3.isPending()); + }); +} + + +function testPromiseFulfilledInsideTask_1() { + var order = []; + + flow.execute(function() { + var d = webdriver.promise.defer(); + + d.promise.then(() => order.push('a')); + d.promise.then(() => order.push('b')); + d.promise.then(() => order.push('c')); + d.fulfill(); + + flow.execute(() => order.push('d')); + + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assertArrayEquals(['a', 'b', 'c', 'd', 'fin'], order); + }); +} + + +function testPromiseFulfilledInsideTask_2() { + var order = []; + + flow.execute(function() { + flow.execute(() => order.push('a')); + flow.execute(() => order.push('b')); + + var d = webdriver.promise.defer(); + d.promise.then(() => order.push('c')); + d.promise.then(() => order.push('d')); + d.fulfill(); + + flow.execute(() => order.push('e')); + + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assertArrayEquals(['a', 'b', 'c', 'd', 'e', 'fin'], order); + }); +} + + +function testPromiseFulfilledInsideTask_3() { + var order = []; + var d = webdriver.promise.defer(); + d.promise.then(() => order.push('c')); + d.promise.then(() => order.push('d')); + + flow.execute(function() { + flow.execute(() => order.push('a')); + flow.execute(() => order.push('b')); + + d.promise.then(() => order.push('e')); + d.fulfill(); + + flow.execute(() => order.push('f')); + + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assertArrayEquals(['c', 'd', 'a', 'b', 'e', 'f', 'fin'], order); + }); +} + + +function testPromiseFulfilledInsideTask_4() { + var order = []; + var d = webdriver.promise.defer(); + d.promise.then(() => order.push('a')); + d.promise.then(() => order.push('b')); + + flow.execute(function() { + flow.execute(function() { + order.push('c'); + flow.execute(() => order.push('d')); + d.promise.then(() => order.push('e')); + }); + flow.execute(() => order.push('f')); + + d.promise.then(() => order.push('g')); + d.fulfill(); + + flow.execute(() => order.push('h')); + + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assertArrayEquals(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'fin'], order); + }); +} + + +function testSettledPromiseCallbacksInsideATask_1() { + var order = []; + var p = webdriver.promise.fulfilled(); + + flow.execute(function() { + flow.execute(() => order.push('a')); + p.then(() => order.push('b')); + flow.execute(() => order.push('c')); + p.then(() => order.push('d')); + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assertArrayEquals(['a', 'b', 'c', 'd', 'fin'], order); + }); +} + +function testSettledPromiseCallbacksInsideATask_2() { + var order = []; + + flow.execute(function() { + flow.execute(() => order.push('a')) + .then( () => order.push('c')); + flow.execute(() => order.push('b')); + }).then(() => order.push('fin')); + + return waitForIdle().then(function() { + assertArrayEquals(['a', 'c', 'b', 'fin'], order); + }); +} diff --git a/lib/webdriver/test/promise_generator_test.js b/lib/webdriver/test/promise_generator_test.js new file mode 100644 index 0000000..708f4cd --- /dev/null +++ b/lib/webdriver/test/promise_generator_test.js @@ -0,0 +1,323 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.provide('webdriver.test.promise.generator.test'); +goog.setTestOnly('webdriver.test.promise.generator.test'); + +goog.require('goog.testing.jsunit'); +goog.require('webdriver.promise'); + + +function testRequiresInputsToBeGeneratorFunctions() { + var thrown = assertThrows(function() { + webdriver.promise.consume(function() {}); + }); + assertTrue(thrown instanceof TypeError); +} + + +function testBasicGenerator() { + var values = []; + return webdriver.promise.consume(function* () { + var i = 0; + while (i < 4) { + i = yield i + 1; + values.push(i); + } + }).then(function() { + assertArrayEquals([1, 2, 3, 4], values); + }); +} + + +function testPromiseYieldingGenerator() { + var values = []; + return webdriver.promise.consume(function* () { + var i = 0; + while (i < 4) { + // Test that things are actually async here. + setTimeout(function() { + values.push(i * 2); + }, 10); + + yield webdriver.promise.delayed(10).then(function() { + values.push(i++); + }); + } + }).then(function() { + assertArrayEquals([0, 0, 2, 1, 4, 2, 6, 3], values); + }); +} + + +function testAssignmentsToYieldedPromisesGetFulfilledValue() { + return webdriver.promise.consume(function* () { + var p = webdriver.promise.fulfilled(2); + var x = yield p; + assertEquals(2, x); + }); +} + + +function testCanCancelPromiseGenerator() { + var values = []; + var p = webdriver.promise.consume(function* () { + var i = 0; + while (i < 3) { + yield webdriver.promise.delayed(100).then(function() { + values.push(i++); + }); + } + }); + return webdriver.promise.delayed(75).then(function() { + p.cancel(); + return p.thenCatch(function() { + return webdriver.promise.delayed(300); + }); + }).then(function() { + assertArrayEquals([0], values); + }); +} + + +function testFinalReturnValueIsUsedAsFulfillmentValue() { + return webdriver.promise.consume(function* () { + yield 1; + yield 2; + return 3; + }).then(function(value) { + assertEquals(3, value); + }); +} + + +function testRejectionsAreThrownWithinGenerator() { + var values = []; + return webdriver.promise.consume(function* () { + values.push('a'); + var e = Error('stub error'); + try { + yield webdriver.promise.rejected(e); + values.push('b'); + } catch (ex) { + assertEquals(e, ex); + values.push('c'); + } + values.push('d'); + }).then(function() { + assertArrayEquals(['a', 'c', 'd'], values); + }); +} + + +function testUnhandledRejectionsAbortGenerator() { + var values = []; + var e = Error('stub error'); + return webdriver.promise.consume(function* () { + values.push(1); + yield webdriver.promise.rejected(e); + values.push(2); + }).thenCatch(function() { + assertArrayEquals([1], values); + }); +} + + +function testYieldsWaitForPromises() { + var values = []; + var d = webdriver.promise.defer(); + + setTimeout(function() { + assertArrayEquals([1], values); + d.fulfill(2); + }, 100); + + return webdriver.promise.consume(function* () { + values.push(1); + values.push((yield d.promise), 3); + }).then(function() { + assertArrayEquals([1, 2, 3], values); + }); +} + + +function testCanSpecifyGeneratorScope() { + return webdriver.promise.consume(function* () { + return this.name; + }, {name: 'Bob'}).then(function(value) { + assertEquals('Bob', value); + }); +} + + +function testCanSpecifyGeneratorArgs() { + return webdriver.promise.consume(function* (a, b) { + assertEquals('red', a); + assertEquals('apples', b); + }, null, 'red', 'apples'); +} + + +function testExecuteGeneratorInAFlow() { + var promises = [ + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + var values = []; + + setTimeout(function() { + assertArrayEquals([], values); + promises[0].fulfill(1); + }, 100); + + setTimeout(function() { + assertArrayEquals([1], values); + promises[1].fulfill(2); + }, 200); + + return webdriver.promise.controlFlow().execute(function* () { + values.push(yield promises[0].promise); + values.push(yield promises[1].promise); + values.push('fin'); + }).then(function() { + assertArrayEquals([1, 2, 'fin'], values); + }); +} + + +function testNestedGeneratorsInAFlow() { + var flow = webdriver.promise.controlFlow(); + return flow.execute(function* () { + var x = yield flow.execute(function() { + return webdriver.promise.delayed(10).then(function() { + return 1; + }); + }); + + var y = yield flow.execute(function() { + return 2; + }); + + return x + y; + }).then(function(value) { + assertEquals(3, value); + }); +} + + +function testFlowWaitOnGenerator() { + var values = []; + return webdriver.promise.controlFlow().wait(function* () { + yield values.push(1); + values.push(yield webdriver.promise.delayed(10).then(function() { + return 2; + })); + yield values.push(3); + return values.length === 6; + }, 250).then(function() { + assertArrayEquals([1, 2, 3, 1, 2, 3], values); + }); +} + + +function testFlowWaitingOnGeneratorTimesOut() { + var values = []; + return webdriver.promise.controlFlow().wait(function* () { + var i = 0; + while (i < 3) { + yield webdriver.promise.delayed(100).then(function() { + values.push(i++); + }); + } + }, 75).thenCatch(function() { + assertArrayEquals('Should complete one loop of wait condition', + [0, 1, 2], values); + }); +} + + +function testGeneratorAsPromiseCallback_1() { + var promises = [ + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + var values = []; + + setTimeout(function() { + promises[0].fulfill(1); + }, 50); + + setTimeout(function() { + promises[1].fulfill(2); + }, 100); + + return webdriver.promise.fulfilled().then(function*() { + values.push(yield promises[0].promise); + values.push(yield promises[1].promise); + values.push('fin'); + }).then(function() { + assertArrayEquals([1, 2, 'fin'], values); + }); +} + + +function testGeneratorAsPromiseCallback_2() { + var promises = [ + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + var values = []; + + setTimeout(function() { + promises[0].fulfill(1); + }, 50); + + setTimeout(function() { + promises[1].fulfill(2); + }, 100); + + return webdriver.promise.fulfilled(3).then(function*(value) { + var p1 = yield promises[0].promise; + var p2 = yield promises[1].promise; + values.push(value + p1); + values.push(value + p2); + values.push('fin'); + }).then(function() { + assertArrayEquals([4, 5, 'fin'], values); + }); +} + +function testGeneratorAsPromiseCallback_3() { + var d = webdriver.promise.defer(); + var e = Error('stub'); + + setTimeout(function() { + d.reject(e); + }, 50); + + return webdriver.promise.fulfilled().then(function*() { + var threw = false; + try { + yield d.promise; + } catch (ex) { + threw = true; + assertEquals(e, ex); + } + assertTrue(threw); + }); +} + diff --git a/lib/webdriver/test/promise_test.js b/lib/webdriver/test/promise_test.js new file mode 100644 index 0000000..cfea256 --- /dev/null +++ b/lib/webdriver/test/promise_test.js @@ -0,0 +1,2012 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.MockClock'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.promise'); +goog.require('webdriver.stacktrace'); +goog.require('webdriver.test.testutil'); + + +// Aliases for readability. +var assertIsPromise = webdriver.test.testutil.assertIsPromise, + assertNotPromise = webdriver.test.testutil.assertNotPromise, + callbackHelper = webdriver.test.testutil.callbackHelper, + callbackPair = webdriver.test.testutil.callbackPair, + assertIsStubError = webdriver.test.testutil.assertIsStubError, + throwStubError = webdriver.test.testutil.throwStubError, + StubError = webdriver.test.testutil.StubError; + +var app, clock, uncaughtExceptions; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + +function setUp() { + webdriver.promise.LONG_STACK_TRACES = false; + clock = new goog.testing.MockClock(true); + uncaughtExceptions = []; + + app = webdriver.promise.controlFlow(); + app.on(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + goog.bind(uncaughtExceptions.push, uncaughtExceptions)); +} + + +function tearDown() { + clock.tick(Infinity); + clock.dispose(); + app.reset(); + webdriver.promise.setDefaultFlow(new webdriver.promise.ControlFlow); + assertArrayEquals( + 'Did not expect any uncaught exceptions', [], uncaughtExceptions); + webdriver.promise.LONG_STACK_TRACES = false; +} + + +function createRejectedPromise(reason) { + var p = webdriver.promise.rejected(reason); + p.thenCatch(goog.nullFunction); + return p; +} + + +function testCanDetectPromiseLikeObjects() { + assertIsPromise(new webdriver.promise.Promise(function(fulfill) { + fulfill(); + })); + assertIsPromise(new webdriver.promise.Deferred()); + assertIsPromise(new webdriver.promise.Deferred().promise); + assertIsPromise({then:function() {}}); + + assertNotPromise(undefined); + assertNotPromise(null); + assertNotPromise(''); + assertNotPromise(true); + assertNotPromise(false); + assertNotPromise(1); + assertNotPromise({}); + assertNotPromise({then:1}); + assertNotPromise({then:true}); + assertNotPromise({then:''}); +} + + +function testSimpleResolveScenario() { + var callback = callbackHelper(function(value) { + assertEquals(123, value); + }); + + var deferred = webdriver.promise.defer(); + deferred.promise.then(callback); + + callback.assertNotCalled(); + deferred.fulfill(123); + clock.tick(); + callback.assertCalled(); +} + + +function testRegisteringACallbackPostResolution() { + var callback, deferred = new webdriver.promise.Deferred(); + + deferred.then((callback = callbackHelper(function(value) { + assertEquals(123, value); + }))); + deferred.fulfill(123); + clock.tick(); + callback.assertCalled(); + + deferred.then((callback = callbackHelper(function(value) { + assertEquals(123, value); + }))); + callback.assertNotCalled(); + clock.tick(); + callback.assertCalled(); +} + + +function testRegisterACallbackViaDeferredPromise() { + var callback, deferred = new webdriver.promise.Deferred(); + + deferred.promise.then((callback = callbackHelper(function(value) { + assertEquals(123, value); + }))); + deferred.fulfill(123); + clock.tick(); + callback.assertCalled(); + + deferred.promise.then((callback = callbackHelper(function(value) { + assertEquals(123, value); + }))); + clock.tick(); + callback.assertCalled(); +} + + +function testTwoStepResolvedChain() { + var callback, start = new webdriver.promise.Deferred(); + + var next = start.then((callback = callbackHelper(function(value) { + assertEquals(123, value); + return value + 1; + }))); + + assertIsPromise(next); + + callback.assertNotCalled(); + start.fulfill(123); + clock.tick(); + callback.assertCalled(); + + next.then((callback = callbackHelper(function(value) { + assertEquals(124, value); + }))); + clock.tick(); + callback.assertCalled(); +} + + +function testCanResolveOnlyOnce_resolved() { + var deferred = new webdriver.promise.Deferred(); + deferred.fulfill(1); + deferred.fulfill(2); + deferred.reject(3); + + var callback; + deferred.then(callback = callbackHelper(function(value) { + assertEquals(1, value); + })); + clock.tick(); + callback.assertCalled(); +} + + +function testCanResolveOnlyOnce_rejected() { + var deferred = new webdriver.promise.Deferred(); + deferred.reject(new StubError); + deferred.fulfill(1); + deferred.reject(2); + + var callback; + deferred.then(null, callback = callbackHelper(assertIsStubError)); + clock.tick(); + callback.assertCalled(); +} + + +function testIfFulfilledWithOtherPromiseCannotChangeValueWhileWaiting() { + var deferred = webdriver.promise.defer(); + var other = webdriver.promise.defer(); + + deferred.fulfill(other.promise); + deferred.fulfill('different value'); + + var callback = callbackHelper(function(value) { + assertEquals(123, value); + }); + + deferred.then(callback); + callback.assertNotCalled(); + + other.fulfill(123); + clock.tick(); + callback.assertCalled(); +} + + +function testOnlyGoesDownListenerPath_resolved() { + var callback = callbackHelper(); + var errback = callbackHelper(); + + webdriver.promise.fulfilled().then(callback, errback); + clock.tick(); + callback.assertCalled(); + errback.assertNotCalled(); +} + + +function testOnlyGoesDownListenerPath_rejected() { + var callback = callbackHelper(); + var errback = callbackHelper(); + + webdriver.promise.rejected().then(callback, errback); + clock.tick(); + callback.assertNotCalled(); + errback.assertCalled(); +} + + +function testCatchingAndSuppressingRejectionErrors() { + var errback = callbackHelper(assertIsStubError); + var callback = callbackHelper(function() { + assertUndefined(arguments[0]); + }); + + webdriver.promise.rejected(new StubError). + thenCatch(errback). + then(callback); + clock.tick(); + errback.assertCalled(); + callback.assertCalled(); +} + + +function testThrowingNewRejectionErrors() { + var errback1 = callbackHelper(assertIsStubError); + var error2 = Error('hi'); + var errback2 = callbackHelper(function(error) { + assertEquals(error2, error); + }); + + webdriver.promise.rejected(new StubError). + thenCatch(function(error) { + errback1(error); + throw error2; + }). + thenCatch(errback2); + clock.tick(); + errback1.assertCalled(); + errback2.assertCalled(); +} + + +function testThenFinally_nonFailingCallbackDoesNotSuppressOriginalError() { + var done = callbackHelper(assertIsStubError); + webdriver.promise.rejected(new StubError). + thenFinally(goog.nullFunction). + thenCatch(done); + clock.tick(); + done.assertCalled(); +} + + +function testThenFinally_failingCallbackSuppressesOriginalError() { + var done = callbackHelper(assertIsStubError); + webdriver.promise.rejected(new Error('original')). + thenFinally(throwStubError). + thenCatch(done); + clock.tick(); + done.assertCalled(); +} + + +function testThenFinally_callbackThrowsAfterFulfilledPromise() { + var done = callbackHelper(assertIsStubError); + webdriver.promise.fulfilled(). + thenFinally(throwStubError). + thenCatch(done); + clock.tick(); + done.assertCalled(); +} + + +function testThenFinally_callbackReturnsRejectedPromise() { + var done = callbackHelper(assertIsStubError); + webdriver.promise.fulfilled(). + thenFinally(function() { + return webdriver.promise.rejected(new StubError); + }). + thenCatch(done); + clock.tick(); + done.assertCalled(); +} + + +function testChainingThen_AllResolved() { + var callbacks = [ + callbackHelper(function(value) { + assertEquals(128, value); + return value * 2; + }), + callbackHelper(function(value) { + assertEquals(256, value); + return value * 2; + }), + callbackHelper(function(value) { + assertEquals(512, value); + }) + ]; + + var deferred = new webdriver.promise.Deferred(); + deferred. + then(callbacks[0]). + then(callbacks[1]). + then(callbacks[2]); + + callbacks[0].assertNotCalled(); + callbacks[1].assertNotCalled(); + callbacks[2].assertNotCalled(); + + deferred.fulfill(128); + + clock.tick(); + callbacks[0].assertCalled(); + callbacks[1].assertCalled(); + callbacks[2].assertCalled(); +} + + +function testWhen_ReturnsAResolvedPromiseIfGivenANonPromiseValue() { + var ret = webdriver.promise.when('abc'); + assertIsPromise(ret); + + var callback; + ret.then(callback = callbackHelper(function (value) { + assertEquals('abc', value); + })); + clock.tick(); + callback.assertCalled(); +} + + +function testWhen_PassesRawErrorsToCallbacks() { + var error = new Error('boo!'), callback; + webdriver.promise.when(error, callback = callbackHelper(function(value) { + assertEquals(error, value); + })); + clock.tick(); + callback.assertCalled(); +} + + +function testWhen_WaitsForValueToBeResolvedBeforeInvokingCallback() { + var d = new webdriver.promise.Deferred(), callback; + webdriver.promise.when(d, callback = callbackHelper(function(value) { + assertEquals('hi', value); + })); + callback.assertNotCalled(); + d.fulfill('hi'); + clock.tick(); + callback.assertCalled(); +} + + +function testWhen_canCancelReturnedPromise() { + var callbacks = callbackPair(null, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('just because', e.message); + }); + + var promiseLike = { + then: function(cb, eb) { + this.callback = cb; + this.errback = eb; + } + }; + + var promise = webdriver.promise.when(promiseLike, + callbacks.callback, callbacks.errback); + + assertTrue(promise.isPending()); + promise.cancel('just because'); + clock.tick(); + callbacks.assertErrback(); + + // The following should have no effect. + promiseLike.callback(); + promiseLike.errback(); +} + + +function testFiresUncaughtExceptionEventIfRejectionNeverHandled() { + webdriver.promise.rejected(new StubError); + var handler = callbackHelper(assertIsStubError); + + // so tearDown() doesn't throw + app.removeAllListeners(); + app.on(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler); + clock.tick(); + handler.assertCalled(); +} + + +function testWaitsIfCallbackReturnsAPromiseObject() { + var callback1, callback2; + var callback1Return = new webdriver.promise.Deferred(); + + webdriver.promise.fulfilled('hi'). + then(callback1 = callbackHelper(function(value) { + assertEquals('hi', value); + return callback1Return; + })). + then(callback2 = callbackHelper(function(value) { + assertEquals('bye', value); + })); + + clock.tick(); + callback1.assertCalled(); + callback2.assertNotCalled(); + callback1Return.fulfill('bye'); + clock.tick(); + callback2.assertCalled(); +} + + +function testWaitsIfCallbackReturnsAPromiseLikeObject() { + var callback1, callback2; + var callback1Return = { + then: function(callback) { + this.callback = callback; + }, + fulfill: function(value) { + this.callback(value); + } + }; + + webdriver.promise.fulfilled('hi'). + then(callback1 = callbackHelper(function(value) { + assertEquals('hi', value); + return callback1Return; + })). + then(callback2 = callbackHelper(function(value) { + assertEquals('bye', value); + })); + + clock.tick(); + callback1.assertCalled(); + callback2.assertNotCalled(); + callback1Return.fulfill('bye'); + clock.tick(); + callback2.assertCalled(); +} + + +function testResolvingAPromiseWithAnotherPromiseCreatesAChain_ourPromise() { + var d1 = new webdriver.promise.Deferred(); + var d2 = new webdriver.promise.Deferred(); + var callback1, callback2; + + d1.then(callback1 = callbackHelper(function(value) { + assertEquals(4, value); + })); + + var d2promise = d2.then(callback2 = callbackHelper(function(value) { + assertEquals(2, value); + return value * 2; + })); + + callback1.assertNotCalled(); + callback2.assertNotCalled(); + + d2.fulfill(2); + clock.tick(); + callback1.assertNotCalled(); + callback2.assertCalled(); + + d1.fulfill(d2promise); + clock.tick(); + callback1.assertCalled(); + callback2.assertCalled(); +} + + +function testResolvingAPromiseWithAnotherPromiseCreatesAChain_otherPromise() { + var d = new webdriver.promise.Deferred(), callback; + d.then(callback = callbackHelper(function(value) { + assertEquals(4, value); + })); + + var otherPromise = { + then: function(callback) { + this.callback = callback; + }, + fulfill: function(value) { + this.callback(value); + } + }; + + callback.assertNotCalled(); + d.fulfill(otherPromise); + otherPromise.fulfill(4); + clock.tick(); + callback.assertCalled(); +} + + +function testRejectForcesValueToAnError_errorInstance() { + var d = webdriver.promise.defer(); + var callback = callbackHelper(assertIsStubError); + + d.thenCatch(callback); + d.reject(new StubError); + clock.tick(); + callback.assertCalled(); +} + + +function testRejectForcesValueToAnError_errorSubTypeInstance() { + var d = webdriver.promise.defer(); + var e = new TypeError('hi'); + var callback = callbackHelper(function(actual) { + assertEquals(e, actual); + }); + + d.thenCatch(callback); + d.reject(e); + clock.tick(); + callback.assertCalled(); +} + + +function testRejectForcesValueToAnError_customErrorInstance() { + var d = webdriver.promise.defer(); + var e = new goog.debug.Error('hi there'); + var callback = callbackHelper(function(actual) { + assertEquals(e, actual); + }); + + d.thenCatch(callback); + d.reject(e); + clock.tick(); + callback.assertCalled(); +} + + +function testRejectForcesValueToAnError_errorLike() { + var d = webdriver.promise.defer(); + var e = {message: 'yolo'}; + var callback = callbackHelper(function(actual) { + assertEquals(e, actual); + }); + + d.thenCatch(callback); + d.reject(e); + clock.tick(); + callback.assertCalled(); +} + + +function testRejectingAPromiseWithAnotherPromise_ourPromise() { + var d1 = new webdriver.promise.Deferred(); + var d2 = new webdriver.promise.Deferred(); + var pair1 = callbackPair(assertIsStubError, null); + var pair2 = callbackPair(function(value) { + assertEquals(2, value); + return new StubError; + }); + + d1.then(fail, function(e) { + assertEquals(d2promise, e); + return d2promise; + }).then(pair1.callback, pair1.errback); + + var d2promise = d2.then(pair2.callback, pair2.errback); + + pair1.assertNeither(); + pair2.assertNeither(); + + d2.fulfill(2); + clock.tick(); + pair1.assertNeither(); + pair2.assertCallback(); + + d1.reject(d2promise); + clock.tick(); + pair1.assertCallback(); +} + + +function testRejectingAPromiseWithAnotherPromise_otherPromise() { + var otherPromise = { + then: function(callback) { + this.callback = callback; + }, + fulfill: function(value) { + this.callback(value); + } + }; + + var pair = callbackPair(null, assertIsStubError); + var d = new webdriver.promise.Deferred(); + d.promise.then(fail, function(e) { + assertEquals(otherPromise, e); + return otherPromise; + }).then(pair.callback, pair.errback); + + d.reject(otherPromise); + clock.tick(); + pair.assertNeither(); + + otherPromise.fulfill(new StubError); + clock.tick(); + pair.assertCallback(); +} + + +function testResolvingADeferredWithAnotherCopiesTheResolvedValue() { + var d1 = new webdriver.promise.Deferred(); + var d2 = new webdriver.promise.Deferred(); + var callback1, callback2; + + d1.then(callback1 = callbackHelper(function(value) { + assertEquals(2, value); + })); + + d2.then(callback2 = callbackHelper(function(value) { + assertEquals(2, value); + return 4; + })); + + d1.fulfill(d2); + clock.tick(); + callback1.assertNotCalled(); + callback2.assertNotCalled(); + + d2.fulfill(2); + clock.tick(); + callback1.assertCalled(); + callback2.assertCalled(); +} + + +function testCannotResolveAPromiseWithItself_fulfill() { + var pair = callbackPair(null, function(e) { + assertTrue(e instanceof TypeError); + }); + + var f, p = new webdriver.promise.Promise(function(fulfill) { + f = fulfill; + }); + p.then(pair.callback, pair.errback); + f(p); + clock.tick(); + pair.assertErrback(); +} + + +function testCannotResolveAPromiseWithItself_reject() { + var pair = callbackPair(null, function(e) { + assertTrue(e instanceof TypeError); + }); + + var r, p = new webdriver.promise.Promise(function(_, reject) { + r = reject; + }); + p.then(pair.callback, pair.errback); + r(p); + clock.tick(); + pair.assertErrback(); +} + + +function testCannotResolveADeferredWithItself() { + var deferred = new webdriver.promise.Deferred(); + assertThrows(goog.bind(deferred.fulfill, deferred, deferred)); + assertThrows(goog.bind(deferred.reject, deferred, deferred)); +} + + +function testSkipsNullPointsInPromiseChain_callbacks() { + var errback1, errback2, callback; + webdriver.promise.fulfilled('hi'). + thenCatch(errback1 = callbackHelper()). + thenCatch(errback2 = callbackHelper()). + then(callback = callbackHelper(function(value) { + assertEquals('hi', value); + })); + + clock.tick(); + errback1.assertNotCalled(); + errback2.assertNotCalled(); + callback.assertCalled(); +} + + +function testSkipsNullPointsInPromiseChain_errbacks() { + var errback1, errback2, callback; + webdriver.promise.fulfilled('hi'). + thenCatch(errback1 = callbackHelper()). + thenCatch(errback2 = callbackHelper()). + then(callback = callbackHelper(function(value) { + assertEquals('hi', value); + })); + + clock.tick(); + errback1.assertNotCalled(); + errback2.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_primitives() { + function runTest(value) { + var callback, errback; + webdriver.promise.fullyResolved(value).then( + callback = callbackHelper(function(resolved) { + assertEquals(value, resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled( + 'Did not expect errback to be called for: ' + value); + callback.assertCalled('Expected callback to be called for: ' + value); + } + + runTest(true); + runTest(goog.nullFunction); + runTest(null); + runTest(123); + runTest('foo bar'); + runTest(undefined); +} + + +function testFullyResolved_arrayOfPrimitives() { + var array = [true, goog.nullFunction, null, 123, '', undefined, 1]; + var callbacks = callbackPair(function(resolved) { + assertEquals(array, resolved); + assertArrayEquals([true, goog.nullFunction, null, 123, '', undefined, 1], + resolved); + }); + + webdriver.promise.fullyResolved(array).then( + callbacks.callback, callbacks.errback); + + clock.tick(); + callbacks.assertCallback(); +} + +function testFullyResolved_nestedArrayOfPrimitives() { + var array = [true, [goog.nullFunction, null, 123], '', undefined]; + var callback, errback; + webdriver.promise.fullyResolved(array).then( + callback = callbackHelper(function(resolved) { + assertEquals(array, resolved); + assertArrayEquals([true, [goog.nullFunction, null, 123], '', undefined], + resolved); + assertArrayEquals([goog.nullFunction, null, 123], resolved[1]); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_arrayWithPromisedPrimitive() { + var callback, errback; + webdriver.promise.fullyResolved([webdriver.promise.fulfilled(123)]).then( + callback = callbackHelper(function(resolved) { + assertArrayEquals([123], resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_promiseResolvesToPrimitive() { + var promise = webdriver.promise.fulfilled(123); + var callback, errback; + webdriver.promise.fullyResolved(promise).then( + callback = callbackHelper(function(resolved) { + assertEquals(123, resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_promiseResolvesToArray() { + var array = [true, [goog.nullFunction, null, 123], '', undefined]; + var promise = webdriver.promise.fulfilled(array); + var callback, errback; + + var result = webdriver.promise.fullyResolved(promise); + result.then( + callback = callbackHelper(function(resolved) { + assertEquals(array, resolved); + assertArrayEquals([true, [goog.nullFunction, null, 123], '', undefined], + resolved); + assertArrayEquals([goog.nullFunction, null, 123], resolved[1]); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_promiseResolvesToArrayWithPromises() { + var nestedPromise = webdriver.promise.fulfilled(123); + var promise = webdriver.promise.fulfilled([true, nestedPromise]); + + var callback, errback; + webdriver.promise.fullyResolved(promise).then( + callback = callbackHelper(function(resolved) { + assertArrayEquals([true, 123], resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_rejectsIfArrayPromiseRejects() { + var nestedPromise = createRejectedPromise(new StubError); + var promise = webdriver.promise.fulfilled([true, nestedPromise]); + + var pair = callbackPair(null, assertIsStubError); + webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testFullyResolved_rejectsOnFirstArrayRejection() { + var e1 = new Error('foo'); + var e2 = new Error('bar'); + var promise = webdriver.promise.fulfilled([ + createRejectedPromise(e1), + createRejectedPromise(e2) + ]); + + var pair = callbackPair(null, function(error) { + assertEquals(e1, error); + }); + webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testFullyResolved_rejectsIfNestedArrayPromiseRejects() { + var promise = webdriver.promise.fulfilled([ + webdriver.promise.fulfilled([ + createRejectedPromise(new StubError) + ]) + ]); + + var pair = callbackPair(null, assertIsStubError); + webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testFullyResolved_simpleHash() { + var hash = {'a': 123}; + + var callback, errback; + webdriver.promise.fullyResolved(hash).then( + callback = callbackHelper(function(resolved) { + assertEquals(hash, resolved); + webdriver.test.testutil.assertObjectEquals({'a': 123}, resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_nestedHash() { + var nestedHash = {'foo':'bar'}; + var hash = {'a': 123, 'b': nestedHash}; + + var callback, errback; + webdriver.promise.fullyResolved(hash).then( + callback = callbackHelper(function(resolved) { + assertEquals(hash, resolved); + webdriver.test.testutil.assertObjectEquals( + {'a': 123, 'b': {'foo': 'bar'}}, resolved); + webdriver.test.testutil.assertObjectEquals(nestedHash, resolved['b']); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_promiseResolvesToSimpleHash() { + var hash = {'a': 123}; + var promise = webdriver.promise.fulfilled(hash); + + var callback, errback; + webdriver.promise.fullyResolved(promise).then( + callback = callbackHelper(function(resolved) { + webdriver.test.testutil.assertObjectEquals(hash, resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_promiseResolvesToNestedHash() { + var nestedHash = {'foo':'bar'}; + var hash = {'a': 123, 'b': nestedHash}; + var promise = webdriver.promise.fulfilled(hash); + + var callback, errback; + webdriver.promise.fullyResolved(promise).then( + callback = callbackHelper(function(resolved) { + webdriver.test.testutil.assertObjectEquals(hash, resolved); + webdriver.test.testutil.assertObjectEquals(nestedHash, resolved['b']); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_promiseResolvesToHashWithPromises() { + var promise = webdriver.promise.fulfilled({ + 'a': webdriver.promise.fulfilled(123) + }); + + var callback, errback; + webdriver.promise.fullyResolved(promise).then( + callback = callbackHelper(function(resolved) { + webdriver.test.testutil.assertObjectEquals({'a': 123}, resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_rejectsIfHashPromiseRejects() { + var promise = webdriver.promise.fulfilled({ + 'a': createRejectedPromise(new StubError) + }); + + var pair = callbackPair(null, assertIsStubError); + webdriver.promise.fullyResolved(promise).then( + pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + +function testFullyResolved_rejectsIfNestedHashPromiseRejects() { + var promise = webdriver.promise.fulfilled({ + 'a': {'b': createRejectedPromise(new StubError)} + }); + + var pair = callbackPair(null, assertIsStubError); + webdriver.promise.fullyResolved(promise).then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testFullyResolved_instantiatedObject() { + function Foo() { + this.bar = 'baz'; + } + var foo = new Foo; + + var callback, errback; + webdriver.promise.fullyResolved(foo).then( + callback = callbackHelper(function(resolvedFoo) { + assertEquals(foo, resolvedFoo); + assertTrue(resolvedFoo instanceof Foo); + webdriver.test.testutil.assertObjectEquals(new Foo, resolvedFoo); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_withEmptyArray() { + var callback, errback; + webdriver.promise.fullyResolved([]).then( + callback = callbackHelper(function(resolved) { + assertArrayEquals([], resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_withEmptyHash() { + var callback, errback; + webdriver.promise.fullyResolved({}).then( + callback = callbackHelper(function(resolved) { + webdriver.test.testutil.assertObjectEquals({}, resolved); + }), + errback = callbackHelper()); + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_arrayWithPromisedHash() { + var obj = {'foo': 'bar'}; + var promise = webdriver.promise.fulfilled(obj); + var array = [promise]; + + var callback, errback; + webdriver.promise.fullyResolved(array).then( + callback = callbackHelper(function(resolved) { + webdriver.test.testutil.assertObjectEquals(resolved, [obj]); + }), + errback = callbackHelper()); + + + clock.tick(); + errback.assertNotCalled(); + callback.assertCalled(); +} + + +function testFullyResolved_aDomElement() { + var e = document.createElement('div'); + var callbacks = callbackPair(function(resolved) { + assertEquals(e, resolved); + }); + + webdriver.promise.fullyResolved(e). + then(callbacks.callback, callbacks.errback); + + clock.tick(); + callbacks.assertCallback(); +} + + +function testCallbackChain_nonSplit() { + var stage1 = callbackPair(), + stage2 = callbackPair(), + stage3 = callbackPair(); + + webdriver.promise.rejected('foo'). + then(stage1.callback, stage1.errback). + then(stage2.callback, stage2.errback). + then(stage3.callback, stage3.errback); + + clock.tick(); + stage1.assertErrback('Wrong function for stage 1'); + stage2.assertCallback('Wrong function for stage 2'); + stage3.assertCallback('Wrong function for final stage'); +} + + +function testCallbackChain_split() { + var stage1 = callbackPair(), + stage2 = callbackPair(), + stage3 = callbackPair(); + + webdriver.promise.rejected('foo'). + then(stage1.callback). + thenCatch(stage1.errback). + then(stage2.callback). + thenCatch(stage2.errback). + then(stage3.callback, stage3.errback); + + clock.tick(); + stage1.assertErrback('Wrong function for stage 1'); + stage2.assertCallback('Wrong function for stage 2'); + stage3.assertCallback('Wrong function for final stage'); +} + + +function testCheckedNodeCall_functionThrows() { + var error = new Error('boom'); + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + }); + + webdriver.promise.checkedNodeCall(function() { + throw error; + }).then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testCheckedNodeCall_functionReturnsAnError() { + var error = new Error('boom'); + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + }); + webdriver.promise.checkedNodeCall(function(callback) { + callback(error); + }).then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); +} + + +function testCheckedNodeCall_functionReturnsSuccess() { + var success = 'success!'; + var pair = callbackPair(function(value) { + assertEquals(success, value); + }); + webdriver.promise.checkedNodeCall(function(callback) { + callback(null, success); + }).then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testCheckedNodeCall_functionReturnsAndThrows() { + var error = new Error('boom'); + var error2 = new Error('boom again'); + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + }); + webdriver.promise.checkedNodeCall(function(callback) { + callback(error); + throw error2; + }).then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); +} + + +function testCheckedNodeCall_functionThrowsAndReturns() { + var error = new Error('boom'); + var error2 = new Error('boom again'); + var pair = callbackPair(null, function(e) { + assertEquals(error2, e); + }); + webdriver.promise.checkedNodeCall(function(callback) { + setTimeout(goog.partial(callback, error), 10); + throw error2; + }).then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); + pair.reset(); + clock.tick(Infinity); + pair.assertNeither(); +} + + +function testCancel_passesTheCancellationReasonToReject() { + var pair = callbackPair(null, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('because i said so', e.message); + }); + var d = new webdriver.promise.Deferred(); + d.then(pair.callback, pair.errback); + d.cancel('because i said so'); + clock.tick(); + pair.assertErrback(); +} + + +function testCancel_canCancelADeferredFromAChainedPromise() { + var pair1 = callbackPair(null, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + assertEquals('because i said so', e.message); + }); + var pair2 = callbackPair(); + + var d = new webdriver.promise.Deferred(); + var p = d.then(pair1.callback, pair1.errback); + p.then(pair2.callback, pair2.errback); + + p.cancel('because i said so'); + clock.tick(); + pair1.assertErrback('The first errback should have fired.'); + pair2.assertCallback(); +} + + +function testCancel_canCancelATimeout() { + var pair = callbackPair(null, function(e) { + assertTrue(e instanceof webdriver.promise.CancellationError); + }); + var p = webdriver.promise.delayed(250). + then(pair.callback, pair.errback); + p.cancel(); + clock.tick(); + pair.assertErrback(); + clock.tick(250); // Just to make sure nothing happens. + pair.assertErrback(); +} + + +function testCancel_cancelIsANoopOnceAPromiseHasBeenFulfilled() { + var p = webdriver.promise.fulfilled(123); + p.cancel(); + + var pair = callbackPair(goog.partial(assertEquals, 123)); + p.then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testCancel_cancelIsANoopOnceAPromiseHasBeenRejected() { + var p = webdriver.promise.rejected(new StubError); + p.cancel(); + + var pair = callbackPair(null, assertIsStubError); + p.then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); +} + + +function testCancel_noopCancelTriggeredOnCallbackOfResolvedPromise() { + var d = webdriver.promise.defer(); + var p = d.promise.then(); + + d.fulfill(); + p.cancel(); // This should not throw. +} + + +function testCallbackRegistersAnotherListener_callbacksConfiguredPreResolve() { + var messages = []; + var d = new webdriver.promise.Deferred(); + d.promise.then(function() { + messages.push('a'); + d.promise.then(function() { + messages.push('c'); + }); + }); + d.promise.then(function() { + messages.push('b'); + }); + d.fulfill(); + clock.tick(); + assertArrayEquals(['a', 'c', 'b'], messages); +} + + +function testCallbackRegistersAnotherListener_callbacksConfiguredPostResolve() { + var messages = []; + var p = webdriver.promise.fulfilled(); + p.then(function() { + messages.push('a'); + p.then(function() { + messages.push('c'); + }); + }); + p.then(function() { + messages.push('b'); + }); + clock.tick(); + assertArrayEquals(['a', 'c', 'b'], messages); +} + + +function testCallbackRegistersAnotherListener_recursive() { + var order = []; + var promise = webdriver.promise.fulfilled(); + + promise.then(function() { + push(); + promise.then(push); + }).then(function() { + push(); + }); + + assertArrayEquals([], order); + clock.tick(); + assertArrayEquals([0, 1, 2], order); + + function push() { + order.push(order.length); + } +} + + +function testCallbackRegistersAnotherListener_recursiveCallbacks_many() { + var messages = []; + var start = 97; // 'a' + + var p = webdriver.promise.fulfilled(); + p.then(push).then(function() { + messages.push('done'); + }); + + function push() { + messages.push(String.fromCharCode(start++)); + if (start != 101) { // 'd' + p.then(push); + } + } + + clock.tick(); + assertArrayEquals(['a', 'b', 'c', 'd', 'done'], messages); +} + + +function testThenReturnsOwnPromiseIfNoCallbacksWereGiven() { + var deferred = new webdriver.promise.Deferred(); + assertEquals(deferred.promise, deferred.promise.then()); + assertEquals(deferred.promise, webdriver.promise.when(deferred.promise)); +} + + +function testIsStillConsideredUnHandledIfNoCallbacksWereGivenOnCallsToThen() { + webdriver.promise.rejected(new StubError).then(); + var handler = callbackHelper(assertIsStubError); + + // so tearDown() doesn't throw + app.removeAllListeners(); + app.on(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, handler); + clock.tick(); + handler.assertCalled(); +} + +function testResolvedReturnsInputValueIfItIsAPromise() { + var input = webdriver.promise.fulfilled('a'); + var output = webdriver.promise.fulfilled(input); + assertEquals(input, output); +} + + +function testADeferredsParentControlFlowIsActiveForCallbacks() { + var defaultFlow = webdriver.promise.controlFlow(); + + var flow1 = new webdriver.promise.ControlFlow(); + var d = new webdriver.promise.Deferred(flow1); + d.fulfill(); + + var flow2 = new webdriver.promise.ControlFlow(); + var d2 = new webdriver.promise.Deferred(flow2); + d2.fulfill(); + + assertIsFlow(defaultFlow); + + var callbacks = callbackPair(); + d.promise.then(assertIsFlow(flow1)). + then(assertIsFlow(flow1)). + then(function() { + return d2.promise.then(assertIsFlow(flow2)); + }). + then(assertIsFlow(flow1)). + then(callbacks.callback, callbacks.errback); + + clock.tick(); + callbacks.assertCallback(); + assertIsFlow(defaultFlow); + + function assertIsFlow(flow) { + return function() { + assertEquals(flow, webdriver.promise.controlFlow()); + }; + } +} + + +function testPromiseAll_emptyArray() { + var pair = callbackPair(function(value) { + assertArrayEquals([], value); + }); + + webdriver.promise.all([]).then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testPromiseAll() { + var a = [ + 0, 1, + webdriver.promise.defer(), + webdriver.promise.defer(), + 4, 5, 6 + ]; + delete a[5]; + + var pair = callbackPair(function(value) { + var expected = [0, 1, 2, 3, 4, 5, 6]; + delete expected[5]; + assertArrayEquals(expected, value); + }); + + webdriver.promise.all(a).then(pair.callback, pair.errback); + pair.assertNeither(); + + a[2].fulfill(2); + pair.assertNeither(); + + a[3].fulfill(3); + clock.tick(); + pair.assertCallback(); +} + + +function testPromiseAll_usesFirstRejection() { + var a = [ + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + + var pair = callbackPair(null, assertIsStubError); + + webdriver.promise.all(a).then(pair.callback, pair.errback); + pair.assertNeither(); + + a[1].reject(new StubError); + clock.tick(); + pair.assertErrback(); + + a[0].reject(Error('ignored')); + clock.tick(Infinity); +} + + +function testMappingAnArray() { + var a = [1, 2, 3]; + var result = webdriver.promise.map(a, function(value, index, a2) { + assertEquals(a, a2); + assertEquals('not a number', 'number', typeof index); + return value + 1; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals([2, 3, 4], value); + }); + + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testMappingAnArray_omitsDeleted() { + var a = [0, 1, 2, 3, 4, 5, 6]; + delete a[1]; + delete a[3]; + delete a[4]; + delete a[6]; + + var result = webdriver.promise.map(a, function(value) { + return value * value; + }); + + var expected = [0, 1, 4, 9, 16, 25, 36]; + delete expected[1]; + delete expected[3]; + delete expected[4]; + delete expected[6]; + + var pair = callbackPair(function(value) { + assertArrayEquals(expected, value); + }); + + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testMappingAnArray_emptyArray() { + var result = webdriver.promise.map([], function(value) { + return value + 1; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals([], value); + }); + + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testMappingAnArray_inputIsPromise() { + var input = webdriver.promise.defer(); + var result = webdriver.promise.map(input, function(value) { + return value + 1; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals([2, 3, 4], value); + }); + + result.then(pair.callback, pair.errback); + pair.assertNeither(); + input.fulfill([1, 2, 3]); + clock.tick(); + pair.assertCallback(); +} + + +function testMappingAnArray_waitsForFunctionResultToResolve() { + var innerResults = [ + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + + var result = webdriver.promise.map([1, 2], function(value, index) { + return innerResults[index].promise; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals(['a', 'b'], value); + }); + + result.then(pair.callback, pair.errback); + pair.assertNeither(); + + innerResults[0].fulfill('a'); + clock.tick(); + pair.assertNeither(); + + innerResults[1].fulfill('b'); + clock.tick(); + pair.assertCallback(); +} + + +function testMappingAnArray_rejectsPromiseIfFunctionThrows() { + var result = webdriver.promise.map([1], throwStubError); + var pair = callbackPair(null, assertIsStubError); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); +} + + +function testMappingAnArray_rejectsPromiseIfFunctionReturnsRejectedPromise() { + var result = webdriver.promise.map([1], function() { + return webdriver.promise.rejected(new StubError); + }); + + var pair = callbackPair(null, assertIsStubError); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); +} + + +function testMappingAnArray_stopsCallingFunctionIfPreviousIterationFailed() { + var count = 0; + var result = webdriver.promise.map([1, 2, 3, 4], function() { + count++; + if (count == 3) { + throw new StubError; + } + }); + + var pair = callbackPair(null, assertIsStubError); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); + assertEquals(3, count); +} + + +function testMappingAnArray_rejectsWithFirstRejectedPromise() { + var innerResult = [ + webdriver.promise.fulfilled(), + createRejectedPromise(new StubError), + createRejectedPromise(Error('should be ignored')) + ]; + var count = 0; + var result = webdriver.promise.map([1, 2, 3, 4], function(value, index) { + count += 1; + return innerResult[index]; + }); + + var pair = callbackPair(null, assertIsStubError); + result.then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); + assertEquals(2, count); +} + + +function testMappingAnArray_preservesOrderWhenMapReturnsPromise() { + var deferreds = [ + webdriver.promise.defer(), + webdriver.promise.defer(), + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + var result = webdriver.promise.map(deferreds, function(value) { + return value.promise; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals([0, 1, 2, 3], value); + }); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertNeither(); + + goog.array.forEachRight(deferreds, function(d, i) { + d.fulfill(i); + }); + clock.tick(); + pair.assertCallback(); +} + + +function testFilteringAnArray() { + var a = [0, 1, 2, 3]; + var result = webdriver.promise.filter(a, function(val, index, a2) { + assertEquals(a, a2); + assertEquals('not a number', 'number', typeof index); + return val > 1; + }); + + var pair = callbackPair(function(val) { + assertArrayEquals([2, 3], val); + }); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testFilteringAnArray_omitsDeleted() { + var a = [0, 1, 2, 3, 4, 5, 6]; + delete a[3]; + delete a[4]; + + var result = webdriver.promise.filter(a, function(value) { + return value > 1 && value < 6; + }); + + var pair = callbackPair(function(val) { + assertArrayEquals([2, 5], val); + }); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testFilteringAnArray_preservesInputs() { + var a = [0, 1, 2, 3]; + + var result = webdriver.promise.filter(a, function(value, i, a2) { + assertEquals(a, a2); + // Even if a function modifies the input array, the original value + // should be inserted into the new array. + a2[i] = a2[i] - 1; + return a2[i] >= 1; + }); + + var pair = callbackPair(function(val) { + assertArrayEquals([2, 3], val); + }); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertCallback(); +} + + +function testFilteringAnArray_inputIsPromise() { + var input = webdriver.promise.defer(); + var result = webdriver.promise.filter(input, function(value) { + return value > 1 && value < 3; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals([2], value); + }); + + result.then(pair.callback, pair.errback); + pair.assertNeither(); + input.fulfill([1, 2, 3]); + clock.tick(); + pair.assertCallback(); +} + + +function testFilteringAnArray_waitsForFunctionResultToResolve() { + var innerResults = [ + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + + var result = webdriver.promise.filter([1, 2], function(value, index) { + return innerResults[index].promise; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals([2], value); + }); + + result.then(pair.callback, pair.errback); + pair.assertNeither(); + + innerResults[0].fulfill(false); + clock.tick(); + pair.assertNeither(); + + innerResults[1].fulfill(true); + clock.tick(); + pair.assertCallback(); +} + + +function testFilteringAnArray_rejectsPromiseIfFunctionReturnsRejectedPromise() { + var result = webdriver.promise.filter([1], function() { + return webdriver.promise.rejected(new StubError); + }); + + var pair = callbackPair(null, assertIsStubError); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); +} + + +function testFilteringAnArray_stopsCallingFunctionIfPreviousIterationFailed() { + var count = 0; + var result = webdriver.promise.filter([1, 2, 3, 4], function() { + count++; + if (count == 3) { + throw new StubError; + } + }); + + var pair = callbackPair(null, assertIsStubError); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); + assertEquals(3, count); +} + + +function testFilteringAnArray_rejectsWithFirstRejectedPromise() { + var innerResult = [ + webdriver.promise.fulfilled(), + createRejectedPromise(new StubError), + createRejectedPromise(Error('should be ignored')) + ]; + var result = webdriver.promise.filter([1, 2, 3, 4], function(value, index) { + assertTrue(index < innerResult.length); + return innerResult[index]; + }); + + var pair = callbackPair(null, assertIsStubError); + result.then(pair.callback, pair.errback); + pair.assertNeither(); + + clock.tick(); + pair.assertErrback(); +} + + +function testFilteringAnArray_preservesOrderWhenFilterReturnsPromise() { + var deferreds = [ + webdriver.promise.defer(), + webdriver.promise.defer(), + webdriver.promise.defer(), + webdriver.promise.defer() + ]; + var result = webdriver.promise.filter([0, 1, 2, 3], function(value, index) { + return deferreds[index].promise; + }); + + var pair = callbackPair(function(value) { + assertArrayEquals([1, 2], value); + }); + result.then(pair.callback, pair.errback); + clock.tick(); + pair.assertNeither(); + + goog.array.forEachRight(deferreds, function(d, i) { + d.fulfill(i > 0 && i < 3); + }); + clock.tick(); + pair.assertCallback(); +} + + +function testAddThenableImplementation() { + function tmp() {} + assertFalse(webdriver.promise.Thenable.isImplementation(new tmp())); + webdriver.promise.Thenable.addImplementation(tmp); + assertTrue(webdriver.promise.Thenable.isImplementation(new tmp())); +} + + +function testLongStackTraces_doesNotAppendStackIfFeatureDisabled() { + webdriver.promise.LONG_STACK_TRACES = false; + + var error = Error('hello'); + var originalStack = error.stack; + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + assertEquals(originalStack, e.stack); + }); + webdriver.promise.rejected(error). + then(fail). + then(fail). + then(fail). + then(pair.callback, pair.errback); + clock.tick(); + pair.assertErrback(); +} + + +function getStackMessages(error) { + var stack = webdriver.stacktrace.getStack(error); + return goog.array.filter(stack.split(/\n/), function(line) { + return /^From: /.test(line); + }); +} + + +function testLongStackTraces_appendsInitialPromiseCreation_resolverThrows() { + webdriver.promise.LONG_STACK_TRACES = true; + + var error = Error('hello'); + var originalStack = '(placeholder; will be overwritten later)'; + + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + if (!goog.isString(originalStack)) { + return; + } + assertNotEquals(originalStack, e.stack); + assertTrue('should start with original stack', + goog.string.startsWith(e.stack, originalStack)); + assertArrayEquals(['From: Promise: new'], getStackMessages(e)); + }); + + new webdriver.promise.Promise(function() { + try { + throw error; + } catch (e) { + originalStack = e.stack; + throw e; + } + }).then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testLongStackTraces_appendsInitialPromiseCreation_rejectCalled() { + webdriver.promise.LONG_STACK_TRACES = true; + + var error = Error('hello'); + var originalStack = error.stack; + + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + if (!goog.isString(originalStack)) { + return; + } + assertNotEquals(originalStack, e.stack); + assertTrue('should start with original stack', + goog.string.startsWith(e.stack, originalStack)); + assertArrayEquals(['From: Promise: new'], getStackMessages(e)); + }); + + new webdriver.promise.Promise(function(_, reject) { + reject(error); + }).then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testLongStackTraces_appendsEachStepToRejectionError() { + webdriver.promise.LONG_STACK_TRACES = true; + + var error = Error('hello'); + var originalStack = '(placeholder; will be overwritten later)'; + + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + if (!goog.isString(originalStack)) { + return; + } + assertNotEquals(originalStack, e.stack); + assertTrue('should start with original stack', + goog.string.startsWith(e.stack, originalStack)); + assertArrayEquals([ + 'From: Promise: new', + 'From: Promise: then', + 'From: Promise: thenCatch', + 'From: Promise: then', + 'From: Promise: thenCatch', + ], getStackMessages(e)); + }); + + new webdriver.promise.Promise(function() { + try { + throw error; + } catch (e) { + originalStack = e.stack; + throw e; + } + }). + then(fail). + thenCatch(function(e) { throw e; }). + then(fail). + thenCatch(function(e) { throw e; }). + then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} + + +function testLongStackTraces_errorOccursInCallbackChain() { + webdriver.promise.LONG_STACK_TRACES = true; + + var error = Error('hello'); + var originalStack = '(placeholder; will be overwritten later)'; + + var pair = callbackPair(null, function(e) { + assertEquals(error, e); + if (!goog.isString(originalStack)) { + return; + } + assertNotEquals(originalStack, e.stack); + assertTrue('should start with original stack', + goog.string.startsWith(e.stack, originalStack)); + assertArrayEquals([ + 'From: Promise: then', + 'From: Promise: thenCatch', + ], getStackMessages(e)); + }); + + webdriver.promise.fulfilled(). + then(goog.nullFunction). + then(goog.nullFunction). + then(function() { + try { + throw error; + } catch (e) { + originalStack = e.stack; + throw e; + } + }). + thenCatch(function(e) { throw e; }). + then(pair.callback, pair.errback); + + clock.tick(); + pair.assertErrback(); +} diff --git a/lib/webdriver/test/stacktrace_test.js b/lib/webdriver/test/stacktrace_test.js new file mode 100644 index 0000000..8dd4d92 --- /dev/null +++ b/lib/webdriver/test/stacktrace_test.js @@ -0,0 +1,480 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('bot.Error'); +goog.require('bot.ErrorCode'); +goog.require('goog.string'); +goog.require('goog.testing.JsUnitException'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.StrictMock'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.stacktrace'); +goog.require('webdriver.stacktrace'); +goog.require('webdriver.test.testutil'); + +var stubs; + +function setUpPage() { + stubs = new goog.testing.PropertyReplacer(); +} + +function tearDown() { + stubs.reset(); +} + + +function assertStackFrame(message, frameOrFrameString, expectedFrame) { + var frame = frameOrFrameString; + if (goog.isString(frame)) { + frame = webdriver.stacktrace.parseStackFrame_(frame); + assertNotNull(message + '\nunable to parse frame: ' + frameOrFrameString, + frame); + } + var diff = []; + for (var prop in expectedFrame) { + if (goog.isString(expectedFrame[prop]) && + frame[prop] !== expectedFrame[prop]) { + diff.push([ + prop, ': <', expectedFrame[prop], '> !== <', frame[prop], '>' + ].join('')); + } + } + if (diff.length) { + fail(message + + '\nfor: <' + frameOrFrameString + '>' + + '\nexpected: <' + expectedFrame + '>' + + '\nbut was: <' + frame + '>' + + '\n ' + + diff.join('\n ')); + } +} + +function assertFrame(frame, file, line) { + // Normalize path for when run through Node.js on Windows. + var url = frame.getUrl().replace(/\\/g, '/'); + assertContains('/' + file, url); + assertEquals(line, frame.getLine()); +} + +function testGetStacktraceFromFile() { + if (!webdriver.stacktrace.BROWSER_SUPPORTED) { + return false; + } + + var stacktrace = webdriver.test.testutil.getStackTrace(); + assertFrame(stacktrace[0], 'testutil.js', 50); + assertFrame(stacktrace[1], 'stacktrace_test.js', 78); +} + +function testGetStacktraceWithUrlOnLine() { + if (!webdriver.stacktrace.BROWSER_SUPPORTED) { + return false; + } + + // The url argument is intentionally ignored here. + function getStacktraceWithUrlArgument(url) { + return webdriver.stacktrace.get(); + } + + var stacktrace = getStacktraceWithUrlArgument('http://www.google.com'); + assertFrame(stacktrace[0], 'stacktrace_test.js', 90); + + stacktrace = getStacktraceWithUrlArgument('http://www.google.com/search'); + assertFrame(stacktrace[0], 'stacktrace_test.js', 90); +} + +function testParseStackFrameInV8() { + assertStackFrame('exception name only (node v0.8)', + ' at Error (unknown source)', + new webdriver.stacktrace.Frame('', 'Error', '', 'unknown source')); + + assertStackFrame('exception name only (chrome v22)', + ' at Error ()', + new webdriver.stacktrace.Frame('', 'Error', '', '')); + + assertStackFrame('context object + function name + url', + ' at Object.assert (file:///.../asserts.js:29:10)', + new webdriver.stacktrace.Frame('Object', 'assert', '', + 'file:///.../asserts.js:29:10')); + + assertStackFrame('context object + function name + file', + ' at Object.assert (asserts.js:29:10)', + new webdriver.stacktrace.Frame('Object', 'assert', '', + 'asserts.js:29:10')); + + assertStackFrame('context object + anonymous function + file', + ' at Interface. (repl.js:182:12)', + new webdriver.stacktrace.Frame('Interface', '', '', + 'repl.js:182:12')); + + assertStackFrame('url only', + ' at http://www.example.com/jsunit.js:117:13', + new webdriver.stacktrace.Frame('', '', '', + 'http://www.example.com/jsunit.js:117:13')); + + assertStackFrame('file only', + ' at repl:1:57', + new webdriver.stacktrace.Frame('', '', '', 'repl:1:57')); + + assertStackFrame('function alias', + ' at [object Object].exec [as execute] (file:///foo)', + new webdriver.stacktrace.Frame('[object Object]', 'exec', + 'execute', 'file:///foo')); + + assertStackFrame('constructor call', + ' at new Class (file:///foo)', + new webdriver.stacktrace.Frame('new ', 'Class', '', 'file:///foo')); + + assertStackFrame('object property constructor call', + ' at new [object Object].foo (repl:1:2)', + new webdriver.stacktrace.Frame('new [object Object]', 'foo', '', + 'repl:1:2')); + + assertStackFrame('namespaced constructor call', + ' at new foo.bar.Class (foo:1:2)', + new webdriver.stacktrace.Frame('new foo.bar', 'Class', '', 'foo:1:2')); + + assertStackFrame('anonymous constructor call', + ' at new (file:///foo)', + new webdriver.stacktrace.Frame('new ', '', '', + 'file:///foo')); + + assertStackFrame('native function call', + ' at Array.forEach (native)', + new webdriver.stacktrace.Frame('Array', 'forEach', '', 'native')); + + assertStackFrame('eval', + ' at foo (eval at file://bar)', + new webdriver.stacktrace.Frame('', 'foo', '', 'eval at file://bar')); + + assertStackFrame('nested anonymous eval', + ' at eval (eval at (unknown source), :2:7)', + new webdriver.stacktrace.Frame('', 'eval', '', + 'eval at (unknown source), :2:7')); + + assertStackFrame('Url containing parentheses', + ' at Object.assert (http://bar:4000/bar.js?value=(a):150:3)', + new webdriver.stacktrace.Frame('Object', 'assert', '', + 'http://bar:4000/bar.js?value=(a):150:3')); + + assertStackFrame('Frame with non-standard leading whitespace (issue 7994)', + ' at module.exports.runCucumber (/local/dir/path)', + new webdriver.stacktrace.Frame('module.exports', 'runCucumber', '', + '/local/dir/path')); +} + +function testParseClosureCanonicalStackFrame() { + assertStackFrame('unknown frame', '> (unknown)', + webdriver.stacktrace.ANONYMOUS_FRAME_); + assertStackFrame('anonymous frame', '> anonymous', + webdriver.stacktrace.ANONYMOUS_FRAME_); + + assertStackFrame('name only', '> foo', + new webdriver.stacktrace.Frame('', 'foo', '', '')); + + assertStackFrame('name and path', '> foo at http://x:123', + new webdriver.stacktrace.Frame('', 'foo', '', 'http://x:123')); + + assertStackFrame('anonymous function with path', + '> anonymous at file:///x/y/z', + new webdriver.stacktrace.Frame('', 'anonymous', '', 'file:///x/y/z')); + + assertStackFrame('anonymous function with v8 path', + '> anonymous at /x/y/z:12:34', + new webdriver.stacktrace.Frame('', 'anonymous', '', '/x/y/z:12:34')); + + assertStackFrame('context and name only', + '> foo.bar', new webdriver.stacktrace.Frame('foo', 'bar', '', '')); + + assertStackFrame('name and alias', + '> foo [as bar]', new webdriver.stacktrace.Frame('', 'foo', 'bar', '')); + + assertStackFrame('context, name, and alias', + '> foo.bar [as baz]', + new webdriver.stacktrace.Frame('foo', 'bar', 'baz', '')); + + assertStackFrame('path only', '> http://x:123', + new webdriver.stacktrace.Frame('', '', '', 'http://x:123')); + + assertStackFrame('name and arguments', + '> foo(arguments)', new webdriver.stacktrace.Frame('', 'foo', '', '')); + + assertStackFrame('full frame', + '> foo.bar(123, "abc") [as baz] at http://x:123', + new webdriver.stacktrace.Frame('foo', 'bar', 'baz', 'http://x:123')); + + assertStackFrame('name and url with sub-domain', + '> foo at http://x.y.z:80/path:1:2', + new webdriver.stacktrace.Frame('', 'foo', '', + 'http://x.y.z:80/path:1:2')); + + assertStackFrame('name and url with sub-domain', + '> foo.bar.baz at http://x.y.z:80/path:1:2', + new webdriver.stacktrace.Frame('foo.bar', 'baz', '', + 'http://x.y.z:80/path:1:2')); +} + +// All test strings are parsed with the conventional and long +// frame algorithms. +function testParseStackFrameInFirefox() { + var frameString = 'Error("Assertion failed")@:0'; + var frame = webdriver.stacktrace.parseStackFrame_(frameString); + var expected = new webdriver.stacktrace.Frame('', 'Error', '', ''); + assertObjectEquals('function name + arguments', expected, frame); + + frame = webdriver.stacktrace.parseLongFirefoxFrame_(frameString); + assertObjectEquals('function name + arguments', expected, frame); + + frameString = '()@file:///foo:42'; + frame = webdriver.stacktrace.parseStackFrame_(frameString); + expected = new webdriver.stacktrace.Frame('', '', '', 'file:///foo:42'); + assertObjectEquals('anonymous function', expected, frame); + + frame = webdriver.stacktrace.parseLongFirefoxFrame_(frameString); + assertObjectEquals('anonymous function', expected, frame); + + frameString = '@javascript:alert(0)'; + frame = webdriver.stacktrace.parseStackFrame_(frameString); + expected = new webdriver.stacktrace.Frame('', '', '', 'javascript:alert(0)'); + assertObjectEquals('anonymous function', expected, frame); + + frame = webdriver.stacktrace.parseLongFirefoxFrame_(frameString); + assertObjectEquals('anonymous function', expected, frame); +} + +function testStringRepresentation() { + var frame = new webdriver.stacktrace.Frame('window', 'foo', 'bar', + 'http://x?a=1&b=2:1'); + assertEquals(' at window.foo [as bar] (http://x?a=1&b=2:1)', + frame.toString()); + + frame = new webdriver.stacktrace.Frame('', 'Error', '', ''); + assertEquals(' at Error ()', frame.toString()); + + assertEquals(' at ', + webdriver.stacktrace.ANONYMOUS_FRAME_.toString()); + + frame = new webdriver.stacktrace.Frame('', '', '', 'http://x:123'); + assertEquals(' at http://x:123', frame.toString()); + + frame = new webdriver.stacktrace.Frame('foo', 'bar', '', 'http://x:123'); + assertEquals(' at foo.bar (http://x:123)', frame.toString()); + + frame = new webdriver.stacktrace.Frame('new ', 'Foo', '', 'http://x:123'); + assertEquals(' at new Foo (http://x:123)', frame.toString()); + + frame = new webdriver.stacktrace.Frame('new foo', 'Bar', '', ''); + assertEquals(' at new foo.Bar ()', frame.toString()); +} + +// Create a stack trace string with one modest record and one long record, +// Verify that all frames are parsed. The length of the long arg is set +// to blow Firefox 3x's stack if put through a RegExp. +function testParsingLongStackTrace() { + var longArg = goog.string.buildString( + '(', goog.string.repeat('x', 1000000), ')'); + var stackTrace = goog.string.buildString( + 'shortFrame()@:0\n', + 'longFrame', + longArg, + '@http://google.com/somescript:0\n'); + var frames = webdriver.stacktrace.parse_(stackTrace); + assertEquals('number of returned frames', 2, frames.length); + var expected = new webdriver.stacktrace.Frame('', 'shortFrame', '', ''); + assertStackFrame('short frame', frames[0], expected); + + expected = new webdriver.stacktrace.Frame( + '', 'longFrame', '', 'http://google.com/somescript:0'); + assertStackFrame('exception name only', frames[1], expected); +} + +function testRemovesV8MessageHeaderBeforeParsingStack() { + var stack = + ' at Color.red (http://x:1234)\n' + + ' at Foo.bar (http://y:5678)'; + + var frames = webdriver.stacktrace.parse_(stack); + assertEquals(2, frames.length); + assertEquals(' at Color.red (http://x:1234)', frames[0].toString()); + assertEquals(' at Foo.bar (http://y:5678)', frames[1].toString()); +} + +function testCanParseClosureJsUnitExceptions() { + stubs.set(goog.testing.stacktrace, 'get', function() { + return '> Color.red at http://x:1234\n' + + '> Foo.bar at http://y:5678'; + }); + + var error = new goog.testing.JsUnitException('stub'); + stubs.reset(); + + var frames = webdriver.stacktrace.parse_(error.stackTrace); + assertEquals(2, frames.length); + assertEquals(' at Color.red (http://x:1234)', frames[0].toString()); + assertEquals(' at Foo.bar (http://y:5678)', frames[1].toString()); +} + +function testFormattingAV8StyleError() { + var errorStub = { + name: 'Error', + message: 'foo', + toString: function() { return 'Error: foo'; }, + stack: + 'Error: foo\n' + + ' at Color.red (http://x:1234)\n' + + ' at Foo.bar (http://y:5678)' + }; + + var ret = webdriver.stacktrace.format(errorStub); + assertEquals(errorStub, ret); + assertEquals([ + 'Error: foo', + ' at Color.red (http://x:1234)', + ' at Foo.bar (http://y:5678)' + ].join('\n'), ret.stack); +} + +function testFormattingAFirefoxStyleError() { + var errorStub = { + name: 'Error', + message: 'boom', + toString: function() { return 'Error: boom'; }, + stack: + 'foo@file:///foo/foo.js:1\n' + + '@file:///bar/bar.js:1' + }; + + var ret = webdriver.stacktrace.format(errorStub); + assertEquals(errorStub, ret); + assertEquals([ + 'Error: boom', + ' at foo (file:///foo/foo.js:1)', + ' at file:///bar/bar.js:1' + ].join('\n'), ret.stack); +} + +function testInsertsAnAnonymousFrameWhenUnableToParse() { + var errorStub = { + name: 'Error', + message: 'boom', + toString: function() { return 'Error: boom'; }, + stack: + 'foo@file:///foo/foo.js:1\n' + + 'this is unparsable garbage\n' + + '@file:///bar/bar.js:1' + }; + + var ret = webdriver.stacktrace.format(errorStub); + assertEquals(errorStub, ret); + assertEquals([ + 'Error: boom', + ' at foo (file:///foo/foo.js:1)', + ' at ', + ' at file:///bar/bar.js:1' + ].join('\n'), ret.stack); +} + +function testFormattingBotErrors() { + var error = new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT, 'boom'); + var expectedStack = [ + 'NoSuchElementError: boom', + ' at Color.red (http://x:1234)', + ' at Foo.bar (http://y:5678)' + ].join('\n'); + error.stack = expectedStack; + + var ret = webdriver.stacktrace.format(error); + assertEquals(ret, error); + assertEquals(expectedStack, error.stack); +} + +function testFormatsUsingNameAndMessageIfAvailable() { + var ret = webdriver.stacktrace.format({ + name: 'TypeError', + message: 'boom' + }); + assertEquals('TypeError: boom\n', ret.stack); + + ret = webdriver.stacktrace.format({ + message: 'boom' + }); + assertEquals('boom\n', ret.stack); + + ret = webdriver.stacktrace.format({ + toString: function() { + return 'Hello world' + } + }); + assertEquals('Hello world\n', ret.stack); + + ret = webdriver.stacktrace.format({ + name: 'TypeError', + message: 'boom', + toString: function() { + return 'Should not use this' + } + }); + assertEquals('TypeError: boom\n', ret.stack); +} + +function testDoesNotFormatErrorIfOriginalStacktraceIsInAnUnexpectedFormat() { + var error = Error('testing'); + var stack = error.stack = [ + 'Error: testing', + '..> at Color.red (http://x:1234)', + '..> at Foo.bar (http://y:5678)' + ].join('\n'); + + var ret = webdriver.stacktrace.format(error); + assertEquals(ret, error); + assertEquals(stack, error.stack); +} + +function testParseStackFrameInIE10() { + assertStackFrame('name and path', + ' at foo (http://bar:4000/bar.js:150:3)', + new webdriver.stacktrace.Frame('', 'foo', '', + 'http://bar:4000/bar.js:150:3')); + + assertStackFrame('Anonymous function', + ' at Anonymous function (http://bar:4000/bar.js:150:3)', + new webdriver.stacktrace.Frame('', 'Anonymous function', '', + 'http://bar:4000/bar.js:150:3')); + + assertStackFrame('Global code', + ' at Global code (http://bar:4000/bar.js:150:3)', + new webdriver.stacktrace.Frame('', 'Global code', '', + 'http://bar:4000/bar.js:150:3')); + + assertStackFrame('eval code', + ' at foo (eval code:150:3)', + new webdriver.stacktrace.Frame('', 'foo', '', 'eval code:150:3')); + + assertStackFrame('nested eval', + ' at eval code (eval code:150:3)', + new webdriver.stacktrace.Frame('', 'eval code', '', 'eval code:150:3')); + + assertStackFrame('Url containing parentheses', + ' at foo (http://bar:4000/bar.js?value=(a):150:3)', + new webdriver.stacktrace.Frame('', 'foo', '', + 'http://bar:4000/bar.js?value=(a):150:3')); + + assertStackFrame('Url ending with parentheses', + ' at foo (http://bar:4000/bar.js?a=))', + new webdriver.stacktrace.Frame('', 'foo', '', + 'http://bar:4000/bar.js?a=)')); +} diff --git a/lib/webdriver/test/test_bootstrap.js b/lib/webdriver/test/test_bootstrap.js new file mode 100644 index 0000000..e429377 --- /dev/null +++ b/lib/webdriver/test/test_bootstrap.js @@ -0,0 +1,67 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Bootstrap file that loads all of the WebDriver scripts in the + * correct order. Once loaded, pages can use {@code goog.require} statements + * from Google Closure to load additional files as needed. + * + * Example Usage: + * + * + * + * + * + * + * + * + * + */ + +(function() { + var scripts = document.getElementsByTagName('script'); + var directoryPath = './'; + var thisFile = 'test_bootstrap.js'; + + for (var i = 0; i < scripts.length; i++) { + var src = scripts[i].src; + var len = src.length; + if (src.substr(len - thisFile.length) == thisFile) { + directoryPath = src.substr(0, len - thisFile.length); + break; + } + } + + // All of the files to load. Files are specified in the order they must be + // loaded, NOT alphabetical order. + var files = [ + '../../../third_party/closure/goog/base.js', + '../../deps.js' + ]; + + for (var j = 0; j < files.length; j++) { + document.write(''); + } +})(); diff --git a/lib/webdriver/test/testing/asserts_test.js b/lib/webdriver/test/testing/asserts_test.js new file mode 100644 index 0000000..c5e0681 --- /dev/null +++ b/lib/webdriver/test/testing/asserts_test.js @@ -0,0 +1,117 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.test.testutil'); +goog.require('webdriver.testing.assert'); +goog.require('webdriver.testing.asserts'); + +var assert = webdriver.testing.assert; +var result; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + +function setUp() { + result = webdriver.test.testutil.callbackPair(); +} + + +function testAssertion_nonPromiseValue_valueMatches() { + assert('foo').equalTo('foo'); + // OK if it does not throw. +} + + +function testAssertion_nonPromiseValue_notValueMatches() { + var a = assert('foo'); + assertThrows(goog.bind(a.equalTo, a, 'bar')); +} + + +function testAssertion_promiseValue_valueMatches() { + return assert(webdriver.promise.fulfilled('foo')).equalTo('foo'); +} + + +function testAssertion_promiseValue_notValueMatches() { + var d = new webdriver.promise.Deferred(); + return assert(webdriver.promise.fulfilled('bar')).equalTo('foo'). + then(fail, goog.nullFunction); +} + + +function testAssertion_promiseValue_promiseRejected() { + var err = Error(); + return assert(webdriver.promise.rejected(err)).equalTo('foo'). + then(fail, function(e) { + assertEquals(err, e); + }); +} + + +function testAssertion_decoration() { + assert('foo').is.equalTo('foo'); + // Ok if no throws. +} + + +function testAssertion_negation() { + var a = assert('false'); + + a.not.equalTo('bar'); // OK if this does not throw. + a.is.not.equalTo('bar'); // OK if this does not throw. + + var notA = a.not; + assertThrows(goog.bind(notA.equalTo, notA, 123)); + + notA = a.is.not; + assertThrows(goog.bind(notA.equalTo, notA, 123)); +} + + +function testApplyMatcher_nonPromiseValue_valueMatches() { + return assertThat('foo', equals('foo')); +} + + +function testApplyMatcher_nonPromiseValue_notValueMatches() { + return assertThat('foo', equals('bar')).then(fail, goog.nullFunction); +} + + +function testApplyMatcher_promiseValue_valueMatches() { + return assertThat(webdriver.promise.fulfilled('foo'), equals('foo')); +} + + +function testApplyMatcher_promiseValue_notValueMatches() { + return assertThat(webdriver.promise.fulfilled('bar'), equals('foo')). + then(fail, goog.nullFunction); +} + + +function testApplyMatcher_promiseValue_promiseRejected() { + var err = Error(); + return assertThat(webdriver.promise.rejected(err), equals('foo')). + then(fail, function(e) { + assertEquals(err, e); + }); +} diff --git a/lib/webdriver/test/testing/client_test.js b/lib/webdriver/test/testing/client_test.js new file mode 100644 index 0000000..f1179ba --- /dev/null +++ b/lib/webdriver/test/testing/client_test.js @@ -0,0 +1,88 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.testing.Client'); + +var FAKE_WINDOW = { + location: { + pathname: '/foo/bar/baz' + } +}; + +function FakeXhr() {} +FakeXhr.prototype.open = goog.nullFunction; +FakeXhr.prototype.send = goog.nullFunction; + +var stubs = new goog.testing.PropertyReplacer; +var control = new goog.testing.MockControl; +var mockXhr; +var client; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + +function setUp() { + client = new webdriver.testing.Client(FAKE_WINDOW); + mockXhr = control.createStrictMock(FakeXhr); + stubs.set(goog.net, 'XmlHttp', function() { + return mockXhr; + }); +} + +function tearDown() { + stubs.reset(); + control.$tearDown(); +} + +function expectToSendEvent(type, data) { + mockXhr.open('POST', '/testevent', true); + mockXhr.send(JSON.stringify({ + 'id': '/foo/bar/baz', + 'type': type, + 'data': data + })); + control.$replayAll(); +} + +function testSendInitEvent() { + expectToSendEvent('INIT', {}); + client.sendInitEvent(); + control.$verifyAll(); +} + +function testSendResultsEvent() { + expectToSendEvent('RESULTS', { + 'isSuccess': false, + 'report': 'boom' + }); + client.sendResultsEvent(false, 'boom'); + control.$verifyAll(); +} + +function testSendScreenshotEvent() { + expectToSendEvent('SCREENSHOT', { + 'name': 'ss01', + 'data': '12412412asdfasf' + }); + client.sendScreenshotEvent('12412412asdfasf', 'ss01'); + control.$verifyAll(); +} diff --git a/lib/webdriver/test/testing/flowtester_test.js b/lib/webdriver/test/testing/flowtester_test.js new file mode 100644 index 0000000..cad21c9 --- /dev/null +++ b/lib/webdriver/test/testing/flowtester_test.js @@ -0,0 +1,204 @@ +// Copyright 2014 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. + +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.jsunit'); +goog.require('webdriver.promise'); +goog.require('webdriver.testing.Clock'); +goog.require('webdriver.testing.promise.FlowTester'); + +var FAKE_TIMER = { + clearInterval: goog.nullFunction, + clearTimeout: goog.nullFunction, + setInterval: goog.nullFunction, + setTimeout: goog.nullFunction +}; + +var originalDefaultFlow = webdriver.promise.controlFlow(); +var originalCreateFlow = webdriver.promise.createFlow; + +var control, mockClock; + +var flowTester; + +function setUp() { + control = new goog.testing.MockControl(); + mockClock = control.createStrictMock(webdriver.testing.Clock); + flowTester = new webdriver.testing.promise.FlowTester( + mockClock, FAKE_TIMER); +} + +function tearDown() { + control.$tearDown(); + flowTester.dispose(); + assertEquals(originalDefaultFlow, webdriver.promise.controlFlow()); + assertEquals(originalCreateFlow, webdriver.promise.createFlow); +} + +function testTurnEventLoopAdvancesClockByEventLoopFrequency() { + mockClock.tick(webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY); + control.$replayAll(); + + flowTester.turnEventLoop(); + control.$verifyAll(); +} + +function captureNewFlow() { + webdriver.promise.createFlow(); + return goog.array.peek(flowTester.allFlows_).flow; +} + +function emitIdle(flow) { + flow.emit(webdriver.promise.ControlFlow.EventType.IDLE); +} + +function emitTask(flow) { + flow.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK); +} + +function emitError(flow, error) { + flow.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + error); +} + +function testVerifySuccess_aSingleFlow() { + var verifySuccess = goog.bind(flowTester.verifySuccess, flowTester); + var flow = webdriver.promise.controlFlow(); + + assertNotThrows('Flow has done nothing yet', verifySuccess); + + emitTask(flow); + assertThrows('Flow is not idle', verifySuccess); + + emitIdle(flow); + assertNotThrows('Flow went idle', verifySuccess); + + emitError(flow, Error()); + assertThrows('Flow had an error', verifySuccess); + + emitIdle(flow); + assertThrows('Flow previously had an error', verifySuccess); +} + +function testVerifySuccess_multipleFlows() { + var verifySuccess = goog.bind(flowTester.verifySuccess, flowTester); + + var flow0 = webdriver.promise.controlFlow(); + assertNotThrows('default throw is idle', verifySuccess); + + var flow1 = captureNewFlow(); + assertThrows('New flows start busy', verifySuccess); + + emitIdle(flow1); + assertNotThrows(verifySuccess); + + assertThrows('Target flow not found', goog.partial(verifySuccess, {})); + + emitError(flow1, Error()); + assertThrows(verifySuccess); + + assertNotThrows( + 'The designated flow is idle and has no errors!', + goog.partial(verifySuccess, flow0)); +} + +function testVerifyFailure() { + var verifyFailure = goog.bind(flowTester.verifyFailure, flowTester); + var flow0 = webdriver.promise.controlFlow(); + + assertThrows('No failures', verifyFailure); + + emitError(flow0, Error()); + assertThrows('Target flow not found', goog.partial(verifyFailure, {})); + assertNotThrows(verifyFailure); + assertNotThrows(goog.partial(verifyFailure, flow0)); + + emitError(flow0, Error()); + assertThrows('multiple failures', verifyFailure); +} + +function testVerifyFailure_multipleFlows() { + var verifyFailure = goog.bind(flowTester.verifyFailure, flowTester); + + var flow0 = webdriver.promise.controlFlow(); + var flow1 = captureNewFlow(); + + emitIdle(flow0); + emitIdle(flow1); + assertThrows(verifyFailure); + + emitError(flow0, Error()); + assertNotThrows(verifyFailure); + assertNotThrows(goog.partial(verifyFailure, flow0)); + assertThrows(goog.partial(verifyFailure, flow1)); + + emitError(flow1, Error()); + assertNotThrows(verifyFailure); + assertNotThrows(goog.partial(verifyFailure, flow0)); + assertNotThrows(goog.partial(verifyFailure, flow1)); +} + +function testGetFailure() { + var getFailure = goog.bind(flowTester.getFailure, flowTester); + + var flow0 = webdriver.promise.controlFlow(); + var flow1 = captureNewFlow(); + + emitIdle(flow0); + emitIdle(flow1); + assertThrows(getFailure); + + var error0 = Error(); + emitError(flow0, error0); + assertEquals(error0, getFailure()); + assertThrows(goog.partial(getFailure, flow1)); + + var error1 = Error(); + emitError(flow1, error1); + assertThrows(getFailure); + assertEquals(error0, getFailure(flow0)); + assertEquals(error1, getFailure(flow1)); +} + +function testAssertStillRunning() { + var assertStillRunning = goog.bind( + flowTester.assertStillRunning, flowTester); + + var flow0 = webdriver.promise.controlFlow(); + var flow1 = captureNewFlow(); + + assertThrows(assertStillRunning); + assertThrows(goog.partial(assertStillRunning, flow0)); + assertNotThrows(goog.partial(assertStillRunning, flow1)); + + emitIdle(flow1); + assertThrows(assertStillRunning); + assertThrows(goog.partial(assertStillRunning, flow0)); + assertThrows(goog.partial(assertStillRunning, flow1)); + + emitTask(flow0); + assertThrows(assertStillRunning); + assertNotThrows(goog.partial(assertStillRunning, flow0)); + assertThrows(goog.partial(assertStillRunning, flow1)); + + emitTask(flow1); + assertNotThrows(assertStillRunning); + assertNotThrows(goog.partial(assertStillRunning, flow0)); + assertNotThrows(goog.partial(assertStillRunning, flow1)); + + emitError(flow0, Error()); + assertThrows(assertStillRunning); + assertThrows(goog.partial(assertStillRunning, flow0)); + assertNotThrows(goog.partial(assertStillRunning, flow1)); +} diff --git a/lib/webdriver/test/testing/testcase_test.js b/lib/webdriver/test/testing/testcase_test.js new file mode 100644 index 0000000..51b54d1 --- /dev/null +++ b/lib/webdriver/test/testing/testcase_test.js @@ -0,0 +1,229 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.Promise'); +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.mockmatchers'); +goog.require('goog.testing.jsunit'); +goog.require('goog.testing.recordFunction'); +goog.require('goog.userAgent'); +goog.require('webdriver.test.testutil'); +goog.require('webdriver.testing.TestCase'); + + +// Aliases for readability. +var IGNORE_ARGUMENT = goog.testing.mockmatchers.ignoreArgument, + IS_ARRAY_ARGUMENT = goog.testing.mockmatchers.isArray, + StubError = webdriver.test.testutil.StubError, + throwStubError = webdriver.test.testutil.throwStubError, + assertIsStubError = webdriver.test.testutil.assertIsStubError; + +var control = new goog.testing.MockControl(); +var mockTestCase, testStub, mockOnComplete, mockOnError, uncaughtExceptions; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + +function setUp() { + // Use one master mock so we can assert execution order. + mockTestCase = control.createStrictMock({ + setUp: goog.nullFunction, + testFn: goog.nullFunction, + tearDown: goog.nullFunction, + onComplete: goog.nullFunction, + onError: goog.nullFunction + }, true); + + mockOnComplete = goog.bind(mockTestCase.onComplete, mockTestCase); + mockOnError = goog.bind(mockTestCase.onError, mockTestCase); + + testStub = { + name: 'testStub', + scope: mockTestCase, + ref: mockTestCase.testFn + }; + + webdriver.test.testutil.messages = []; + uncaughtExceptions = []; + + webdriver.promise.controlFlow(). + on('uncaughtExceptions', onUncaughtException); +} + +function tearDown() { + var flow = webdriver.promise.controlFlow(); + return new goog.Promise(function(fulfill) { + flow.execute(goog.nullFunction); // Flush. + flow.once('idle', fulfill); + }).then(function() { + assertArrayEquals('There were uncaught exceptions', + [], uncaughtExceptions); + control.$tearDown(); + flow.reset(); + }); +} + +function onUncaughtException(e) { + uncaughtExceptions.push(e); +} + +function schedule(msg, opt_fn) { + var fn = opt_fn || goog.nullFunction; + return webdriver.promise.controlFlow().execute(fn, msg); +} + +function runTest() { + return webdriver.testing.TestCase.prototype.runSingleTest_. + call(mockTestCase, testStub, mockOnError). + then(mockOnComplete); +} + +function testExecutesTheBasicTestFlow() { + mockTestCase.setUp(); + mockTestCase.testFn(); + mockTestCase.tearDown(); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testExecutingAHappyTestWithScheduledActions() { + mockTestCase.setUp().$does(function() { schedule('a'); }); + mockTestCase.testFn().$does(function() { schedule('b'); }); + mockTestCase.tearDown().$does(function() { schedule('c'); }); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testShouldSkipTestFnIfSetupThrows() { + var e = Error(); + mockTestCase.setUp().$does(function() { throw e; }); + mockOnError(e); + mockTestCase.tearDown(); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testShouldSkipTestFnIfSetupActionFails_1() { + var e = Error(); + mockTestCase.setUp().$does(function() { + schedule('an explosion', function() { throw e; }); + }); + mockOnError(e); + mockTestCase.tearDown(); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testShouldSkipTestFnIfSetupActionFails_2() { + var e = Error(); + mockTestCase.setUp().$does(function() { + schedule('an explosion', function() { throw e; }); + }); + mockOnError(e); + mockTestCase.tearDown(); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testShouldSkipTestFnIfNestedSetupActionFails() { + var e = Error(); + mockTestCase.setUp().$does(function() { + schedule('a', goog.nullFunction).then(function() { + schedule('b', function() { throw e; }); + }); + }); + mockOnError(e); + mockTestCase.tearDown(); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testRunsAllTasksForEachPhaseBeforeTheNextPhase() { + webdriver.test.testutil.messages = []; + mockTestCase.setUp().$does(function() { schedule('a'); }); + mockTestCase.testFn().$does(function() { schedule('b'); }); + mockTestCase.tearDown().$does(function() { schedule('c'); }); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testRecordsErrorsFromTestFnBeforeTearDown() { + var e = Error(); + mockTestCase.setUp(); + mockTestCase.testFn().$does(function() { throw e; }); + mockOnError(e); + mockTestCase.tearDown(); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testRecordsErrorsFromTearDown() { + var e = Error(); + mockTestCase.setUp(); + mockTestCase.testFn(); + mockTestCase.tearDown().$does(function() { throw e; }); + mockOnError(e); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testErrorFromSetUpAndTearDown() { + var e1 = Error(); + var e2 = Error(); + mockTestCase.setUp().$does(function() { throw e1; }); + mockOnError(e1); + mockTestCase.tearDown().$does(function() { throw e2; }); + mockOnError(e2); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} + +function testErrorFromTestFnAndTearDown() { + var e1 = Error(), e2 = Error(); + mockTestCase.setUp(); + mockTestCase.testFn().$does(function() { throw e1; }); + mockOnError(e1); + mockTestCase.tearDown().$does(function() { throw e2; }); + mockOnError(e2); + mockOnComplete(IGNORE_ARGUMENT); + control.$replayAll(); + + return runTest(); +} diff --git a/lib/webdriver/test/testutil.js b/lib/webdriver/test/testutil.js new file mode 100644 index 0000000..2cf02a5 --- /dev/null +++ b/lib/webdriver/test/testutil.js @@ -0,0 +1,209 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.provide('webdriver.test.testutil'); +goog.provide('webdriver.test.testutil.StubError'); + +goog.require('goog.array'); +goog.require('goog.debug.Error'); +goog.require('goog.string'); +goog.require('goog.testing.recordFunction'); +goog.require('webdriver.stacktrace'); + + + +/** + * A custom error used for testing. + * @param {string=} opt_msg The error message to use. + * @constructor + * @extends {goog.debug.Error} + * @final + */ +webdriver.test.testutil.StubError = function(opt_msg) { + webdriver.test.testutil.StubError.base(this, 'constructor', opt_msg); +}; +goog.inherits(webdriver.test.testutil.StubError, goog.debug.Error); + + +/** @override */ +webdriver.test.testutil.StubError.prototype.name = 'StubError'; + + +/** @type {Array.} */ +webdriver.test.testutil.messages = []; + +webdriver.test.testutil.getStackTrace = function() { + return webdriver.stacktrace.get(); +}; + +webdriver.test.testutil.throwStubError = function() { + throw new webdriver.test.testutil.StubError; +}; + +webdriver.test.testutil.assertIsStubError = function(error) { + assertTrue(error + ' is not an instanceof StubError', + error instanceof webdriver.test.testutil.StubError); +}; + + +/** + * Asserts the contents of the {@link webdriver.test.testutil.messages} array + * are as expected. + * @param {...*} var_args The expected contents. + */ +webdriver.test.testutil.assertMessages = function(var_args) { + var args = Array.prototype.slice.call(arguments, 0); + assertArrayEquals(args, webdriver.test.testutil.messages); +}; + + +/** + * Wraps a call to {@link webdriver.test.testutil.assertMessages} so it can + * be passed as a callback. + * @param {...*} var_args The expected contents. + * @return {!Function} The wrapped function. + */ +webdriver.test.testutil.assertingMessages = function(var_args) { + var args = goog.array.slice(arguments, 0); + return function() { + return webdriver.test.testutil.assertMessages.apply(null, args); + }; +}; + + +/** + * Asserts an object is a promise. + * @param {*} obj The object to check. + */ +webdriver.test.testutil.assertIsPromise = function(obj) { + assertTrue('Value is not a promise: ' + goog.typeOf(obj), + webdriver.promise.isPromise(obj)); +}; + + +/** + * Asserts an object is not a promise. + * @param {*} obj The object to check. + */ +webdriver.test.testutil.assertNotPromise = function(obj) { + assertFalse(webdriver.promise.isPromise(obj)); +}; + +/** + * Wraps a function. The wrapped function will have several utility functions: + *

          + *
        • assertCalled: Asserts that the function was called. + *
        • assertNotCalled: Asserts that the function was not called. + *
        + * @param {Function=} opt_fn The function to wrap; defaults to + * goog.nullFunction. + * @return {!Function} The wrapped function. + * @see goog.testing.recordFunction + */ +webdriver.test.testutil.callbackHelper = function(opt_fn) { + var callback = goog.testing.recordFunction(opt_fn); + + callback.getExpectedCallCountMessage = function(n, opt_prefix, opt_noJoin) { + var message = []; + if (opt_prefix) message.push(opt_prefix); + + var calls = callback.getCalls(); + message.push( + 'Expected to be called ' + n + ' times.', + ' was called ' + calls.length + ' times:'); + goog.array.forEach(calls, function(call) { + var e = call.getError(); + if (e) { + throw e; + } + }); + return opt_noJoin ? message : message.join('\n'); + }; + + callback.assertCalled = function(opt_message) { + assertEquals(callback.getExpectedCallCountMessage(1, opt_message), + 1, callback.getCallCount()); + }; + + callback.assertNotCalled = function(opt_message) { + assertEquals(callback.getExpectedCallCountMessage(0, opt_message), + 0, callback.getCallCount()); + }; + + return callback; +}; + + +/** + * Creates a utility for managing a pair of callbacks, capable of asserting only + * one of the pair was ever called. + * + * @param {Function=} opt_callback The callback to manage. + * @param {Function=} opt_errback The errback to manage. + */ +webdriver.test.testutil.callbackPair = function(opt_callback, opt_errback) { + var pair = { + callback: webdriver.test.testutil.callbackHelper(opt_callback), + errback: webdriver.test.testutil.callbackHelper(opt_errback) + }; + + /** @param {string=} opt_message Optional failure message. */ + pair.assertEither = function(opt_message) { + if (!pair.callback.getCallCount() && + !pair.errback.getCallCount()) { + var message = ['Neither callback nor errback has been called']; + if (opt_message) goog.array.insertAt(message, opt_message); + fail(message.join('\n')); + } + }; + + /** @param {string=} opt_message Optional failure message. */ + pair.assertNeither = function(opt_message) { + var message = (opt_message || '') + 'Did not expect callback or errback'; + pair.callback.assertNotCalled(message); + pair.errback.assertNotCalled(message); + }; + + /** @param {string=} opt_message Optional failure message. */ + pair.assertCallback = function(opt_message) { + var message = opt_message ? (opt_message + ': ') : ''; + pair.errback.assertNotCalled(message + 'Expected callback, not errback'); + pair.callback.assertCalled(message + 'Callback not called'); + }; + + /** @param {string=} opt_message Optional failure message. */ + pair.assertErrback = function(opt_message) { + var message = opt_message ? (opt_message + ': ') : ''; + pair.callback.assertNotCalled(message + 'Expected errback, not callback'); + pair.errback.assertCalled(message + 'Errback not called'); + }; + + pair.reset = function() { + pair.callback.reset(); + pair.errback.reset(); + }; + + return pair; +}; + + +webdriver.test.testutil.assertObjectEquals = function(expected, actual) { + assertObjectEquals( + 'Expected: ' + JSON.stringify(expected) + '\n' + + 'Actual: ' + JSON.stringify(actual), + expected, actual); +}; diff --git a/lib/webdriver/test/testutil_test.js b/lib/webdriver/test/testutil_test.js new file mode 100644 index 0000000..af55b32 --- /dev/null +++ b/lib/webdriver/test/testutil_test.js @@ -0,0 +1,104 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('goog.testing.jsunit'); +goog.require('webdriver.test.testutil'); + +// Aliases for readability. +var callbackHelper = webdriver.test.testutil.callbackHelper, + callbackPair = webdriver.test.testutil.callbackPair; + +function testCallbackHelper_functionCalled() { + var callback = callbackHelper(); + callback(); + assertNotThrows(callback.assertCalled); + assertThrows(callback.assertNotCalled); +} + +function testCallbackHelper_functionCalledMoreThanOnce() { + var callback = callbackHelper(); + callback(); + callback(123, 'abc'); + assertThrows(callback.assertCalled); + assertThrows(callback.assertNotCalled); +} + +function testCallbackHelper_functionNotCalled() { + var callback = callbackHelper(); + assertNotThrows(callback.assertNotCalled); + assertThrows(callback.assertCalled); +} + +function testCallbackHelper_wrappedFunctionIsCalled() { + var count = 0; + var callback = callbackHelper(function() { + count += 1; + }); + callback(); + assertNotThrows(callback.assertCalled); + assertThrows(callback.assertNotCalled); + assertEquals(1, count); +} + +function testCallbackPair_callbackExpected() { + var pair = callbackPair(); + assertThrows(pair.assertCallback); + pair.callback(); + assertNotThrows(pair.assertCallback); + pair.errback(); + assertThrows(pair.assertCallback); + + pair.reset(); + pair.callback(); + assertNotThrows(pair.assertCallback); + pair.callback(); + assertThrows('Should expect to be called only once', + pair.assertCallback); +} + +function testCallbackPair_errbackExpected() { + var pair = callbackPair(); + assertThrows(pair.assertErrback); + pair.errback(); + assertNotThrows(pair.assertErrback); + pair.callback(); + assertThrows(pair.assertErrback); +} + +function testCallbackPair_eitherExpected() { + var pair = callbackPair(); + assertThrows(pair.assertEither); + pair.errback(); + assertNotThrows(pair.assertEither); + pair.reset(); + pair.callback(); + assertNotThrows(pair.assertEither); + pair.errback(); + assertNotThrows(pair.assertEither); +} + +function testCallbackPair_neitherExpected() { + var pair = callbackPair(); + assertNotThrows(pair.assertNeither); + pair.errback(); + assertThrows(pair.assertNeither); + pair.reset(); + pair.callback(); + assertThrows(pair.assertNeither); + pair.errback(); + assertThrows(pair.assertNeither); +} diff --git a/lib/webdriver/test/until_test.js b/lib/webdriver/test/until_test.js new file mode 100644 index 0000000..e4af619 --- /dev/null +++ b/lib/webdriver/test/until_test.js @@ -0,0 +1,411 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.provide('webdriver.test.until_test'); +goog.setTestOnly('webdriver.test.until_test'); + +goog.require('bot.Error'); +goog.require('bot.ErrorCode'); +goog.require('bot.response'); +goog.require('goog.array'); +goog.require('goog.string'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.By'); +goog.require('webdriver.CommandName'); +goog.require('webdriver.WebDriver'); +goog.require('webdriver.WebElement'); +goog.require('webdriver.WebElementPromise'); +goog.require('webdriver.until'); + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + +var By = webdriver.By; +var until = webdriver.until; +var CommandName = webdriver.CommandName; + +var driver, executor; + +var TestExecutor = function() { + this.handlers_ = {}; +}; + + +TestExecutor.prototype.on = function(cmd, handler) { + this.handlers_[cmd] = handler; + return this; +}; + + +TestExecutor.prototype.execute = function(cmd, callback) { + if (!this.handlers_[cmd.getName()]) { + throw Error('Unsupported command: ' + cmd.getName()); + } + this.handlers_[cmd.getName()](cmd, callback); +}; + + +function setUp() { + executor = new TestExecutor(); + driver = new webdriver.WebDriver('session-id', executor); +} + + +function testUntilAbleToSwitchToFrame_failsFastForNonSwitchErrors() { + var e = Error('boom'); + executor.on(CommandName.SWITCH_TO_FRAME, function(cmd, callback) { + callback(e); + }); + + return driver.wait(until.ableToSwitchToFrame(0), 100) + .then(fail, function(e2) { + assertEquals(e, e2); + }); +} + + +function testUntilAbleToSwitchToFrame_byIndex() { + executor.on(CommandName.SWITCH_TO_FRAME, function(cmd, callback) { + callback(null, {status: bot.ErrorCode.SUCCESS}); + }); + + return driver.wait(until.ableToSwitchToFrame(0), 100); +} + + +function testUntilAbleToSwitchToFrame_byWebElement() { + executor.on(CommandName.SWITCH_TO_FRAME, function(cmd, callback) { + callback(null, {status: bot.ErrorCode.SUCCESS}); + }); + + var el = new webdriver.WebElement(driver, {ELEMENT: 1234}); + return driver.wait(until.ableToSwitchToFrame(el), 100); +} + + +function testUntilAbleToSwitchToFrame_byWebElementPromise() { + executor.on(CommandName.SWITCH_TO_FRAME, function(cmd, callback) { + callback(null, {status: bot.ErrorCode.SUCCESS}); + }); + + var el = new webdriver.WebElementPromise(driver, + webdriver.promise.fulfilled( + new webdriver.WebElement(driver, {ELEMENT: 1234}))); + return driver.wait(until.ableToSwitchToFrame(el), 100); +} + + +function testUntilAbleToSwitchToFrame_byLocator() { + executor.on(CommandName.FIND_ELEMENTS, function(cmd, callback) { + callback(null, { + status: bot.ErrorCode.SUCCESS, + value: [{ELEMENT: 1234}] + }); + }).on(CommandName.SWITCH_TO_FRAME, function(cmd, callback) { + callback(null, {status: bot.ErrorCode.SUCCESS}); + }); + + return driver.wait(until.ableToSwitchToFrame(By.id('foo')), 100); +} + + +function testUntilAbleToSwitchToFrame_byLocator_elementNotInitiallyFound() { + var foundResponses = [[], [], [{ELEMENT: 1234}]]; + executor.on(CommandName.FIND_ELEMENTS, function(cmd, callback) { + callback(null, { + status: bot.ErrorCode.SUCCESS, + value: foundResponses.shift() + }); + }).on(CommandName.SWITCH_TO_FRAME, function(cmd, callback) { + callback(null, {status: bot.ErrorCode.SUCCESS}); + }); + + return driver.wait(until.ableToSwitchToFrame(By.id('foo')), 2000) + .then(function() { + assertEquals(0, foundResponses.length); + }); +} + + +function testUntilAbleToSwitchToFrame_timesOutIfNeverAbletoSwitchFrames() { + var count = 0; + executor.on(CommandName.SWITCH_TO_FRAME, function(cmd, callback) { + count += 1; + callback(null, {status: bot.ErrorCode.NO_SUCH_FRAME}); + }); + + return driver.wait(until.ableToSwitchToFrame(0), 100).then(fail, function(e) { + assertTrue(count > 0); + assertTrue('Wrong message: ' + e.message, goog.string.startsWith( + e.message, 'Waiting to be able to switch to frame')); + }); +} + + +function testUntilAlertIsPresent_failsFastForNonAlertSwitchErrors() { + return driver.wait(until.alertIsPresent(), 100).then(fail, function(e) { + assertEquals( + 'Unsupported command: ' + CommandName.GET_ALERT_TEXT, e.message); + }); +} + + +function testUntilAlertIsPresent() { + var count = 0; + executor.on(CommandName.GET_ALERT_TEXT, function(cmd, callback) { + if (count++ < 3) { + callback(null, {status: bot.ErrorCode.NO_SUCH_ALERT}); + } else { + callback(null, {status: bot.ErrorCode.SUCCESS}); + } + }).on(CommandName.DISMISS_ALERT, function(cmd, callback) { + callback(null, {status: bot.ErrorCode.SUCCESS}); + }); + + return driver.wait(until.alertIsPresent(), 1000).then(function(alert) { + assertEquals(4, count); + return alert.dismiss(); + }); +} + + +function testUntilTitleIs() { + var titles = ['foo', 'bar', 'baz']; + executor.on(CommandName.GET_TITLE, function(cmd, callback) { + callback(null, bot.response.createResponse(titles.shift())); + }); + + return driver.wait(until.titleIs('bar'), 3000).then(function() { + assertArrayEquals(['baz'], titles); + }); +} + + +function testUntilTitleContains() { + var titles = ['foo', 'froogle', 'google']; + executor.on(CommandName.GET_TITLE, function(cmd, callback) { + callback(null, bot.response.createResponse(titles.shift())); + }); + + return driver.wait(until.titleContains('oogle'), 3000).then(function() { + assertArrayEquals(['google'], titles); + }); +} + + +function testUntilTitleMatches() { + var titles = ['foo', 'froogle', 'aaaabc', 'aabbbc', 'google']; + executor.on(CommandName.GET_TITLE, function(cmd, callback) { + callback(null, bot.response.createResponse(titles.shift())); + }); + + return driver.wait(until.titleMatches(/^a{2,3}b+c$/), 3000).then(function() { + assertArrayEquals(['google'], titles); + }); +} + + +function testUntilElementLocated() { + var responses = [[], [{ELEMENT: 'abc123'}, {ELEMENT: 'foo'}], ['end']]; + executor.on(CommandName.FIND_ELEMENTS, function(cmd, callback) { + callback(null, bot.response.createResponse(responses.shift())); + }); + + return driver.wait(until.elementLocated(By.id('quux')), 2000) + .then(function(el) { + return el.getId(); + }).then(function(id) { + assertArrayEquals([['end']], responses); + assertEquals('abc123', id['ELEMENT']); + }); +} + +function runNoElementFoundTest(locator, locatorStr) { + executor.on(CommandName.FIND_ELEMENTS, function(cmd, callback) { + callback(null, bot.response.createResponse([])); + }); + + function expectedFailure() { + fail('expected condition to timeout'); + } + + return driver.wait(until.elementLocated(locator), 100) + .then(expectedFailure, function(error) { + var expected = 'Waiting for element to be located ' + locatorStr; + var lines = error.message.split(/\n/, 2); + assertEquals(expected, lines[0]); + assertRegExp(/^Wait timed out after \d+ms$/, lines[1]); + }); +} + +function testUntilElementLocated_elementNeverFound_byLocator() { + return runNoElementFoundTest(By.id('quux'), 'By.id("quux")'); +} + +function testUntilElementLocated_elementNeverFound_byHash() { + return runNoElementFoundTest({id: 'quux'}, 'By.id("quux")'); +} + +function testUntilElementLocated_elementNeverFound_byFunction() { + return runNoElementFoundTest(goog.nullFunction, 'by function()'); +} + +function testUntilElementsLocated() { + var responses = [[], [{ELEMENT: 'abc123'}, {ELEMENT: 'foo'}], ['end']]; + executor.on(CommandName.FIND_ELEMENTS, function(cmd, callback) { + callback(null, bot.response.createResponse(responses.shift())); + }); + + return driver.wait(until.elementsLocated(By.id('quux')), 2000) + .then(function(els) { + return webdriver.promise.all(goog.array.map(els, function(el) { + return el.getId(); + })); + }).then(function(ids) { + assertArrayEquals([['end']], responses); + assertEquals(2, ids.length); + assertEquals('abc123', ids[0]['ELEMENT']); + assertEquals('foo', ids[1]['ELEMENT']); + }); +} + +function runNoElementsFoundTest(locator, locatorStr) { + executor.on(CommandName.FIND_ELEMENTS, function(cmd, callback) { + callback(null, bot.response.createResponse([])); + }); + + function expectedFailure() { + fail('expected condition to timeout'); + } + + return driver.wait(until.elementsLocated(locator), 100) + .then(expectedFailure, function(error) { + var expected = + 'Waiting for at least one element to be located ' + locatorStr; + var lines = error.message.split(/\n/, 2); + assertEquals(expected, lines[0]); + assertRegExp(/^Wait timed out after \d+ms$/, lines[1]); + }); +} + +function testUntilElementsLocated_noElementsEverFound_byLocator() { + return runNoElementsFoundTest(By.id('quux'), 'By.id("quux")'); +} + +function testUntilElementsLocated_noElementsEverFound_byHash() { + return runNoElementsFoundTest({id: 'quux'}, 'By.id("quux")'); +} + +function testUntilElementsLocated_noElementsEverFound_byFunction() { + return runNoElementsFoundTest(goog.nullFunction, 'by function()'); +} + +function testUntilStalenessOf() { + var responses = [ + bot.response.createResponse('body'), + bot.response.createResponse('body'), + bot.response.createResponse('body'), + bot.response.createErrorResponse( + new bot.Error(bot.ErrorCode.STALE_ELEMENT_REFERENCE, 'now stale')), + ['end'] + ]; + executor.on(CommandName.GET_ELEMENT_TAG_NAME, function(cmd, callback) { + callback(null, responses.shift()); + }); + + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return driver.wait(until.stalenessOf(el), 2000).then(function() { + assertArrayEquals([['end']], responses); + }); +} + +function runElementStateTest(predicate, command, responses) { + assertTrue(responses.length > 1); + + responses = goog.array.concat(responses, ['end']); + executor.on(command, function(cmd, callback) { + callback(null, bot.response.createResponse(responses.shift())); + }); + return driver.wait(predicate, 2000).then(function() { + assertArrayEquals(['end'], responses); + }); +} + +function testUntilElementIsVisible() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementIsVisible(el), + CommandName.IS_ELEMENT_DISPLAYED, [false, false, true]); +} + + +function testUntilElementIsNotVisible() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementIsNotVisible(el), + CommandName.IS_ELEMENT_DISPLAYED, [true, true, false]); +} + + +function testUntilElementIsEnabled() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementIsEnabled(el), + CommandName.IS_ELEMENT_ENABLED, [false, false, true]); +} + + +function testUntilElementIsDisabled() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementIsDisabled(el), + CommandName.IS_ELEMENT_ENABLED, [true, true, false]); +} + + +function testUntilElementIsSelected() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementIsSelected(el), + CommandName.IS_ELEMENT_SELECTED, [false, false, true]); +} + + +function testUntilElementIsNotSelected() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementIsNotSelected(el), + CommandName.IS_ELEMENT_SELECTED, [true, true, false]); +} + + +function testUntilElementTextIs() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementTextIs(el, 'foobar'), + CommandName.GET_ELEMENT_TEXT, ['foo', 'fooba', 'foobar']); +} + + +function testUntilElementTextContains() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementTextContains(el, 'bar'), + CommandName.GET_ELEMENT_TEXT, ['foo', 'foobaz', 'foobarbaz']); +} + + +function testUntilElementTextMatches() { + var el = new webdriver.WebElement(driver, {ELEMENT: 'foo'}); + return runElementStateTest(until.elementTextMatches(el, /fo+bar{3}/), + CommandName.GET_ELEMENT_TEXT, ['foo', 'foobar', 'fooobarrr']); +} diff --git a/lib/webdriver/test/webdriver_generator_test.js b/lib/webdriver/test/webdriver_generator_test.js new file mode 100644 index 0000000..7b04ea4 --- /dev/null +++ b/lib/webdriver/test/webdriver_generator_test.js @@ -0,0 +1,92 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.provide('webdriver.test.WebDriver.generator.test'); +goog.setTestOnly('webdriver.test.WebDriver.generator.test'); + +goog.require('goog.testing.jsunit'); +goog.require('webdriver.Session'); +goog.require('webdriver.WebDriver'); + + +var driver; + +function setUp() { + driver = new webdriver.WebDriver( + new webdriver.Session('test-session', {}), + new ExplodingExecutor()); +} + + +function testCanUseGeneratorsWithWebDriverCall() { + return driver.call(function* () { + var x = yield webdriver.promise.fulfilled(1); + var y = yield webdriver.promise.fulfilled(2); + return x + y; + }).then(function(value) { + assertEquals(3, value); + }); +} + + +function testCanDefineScopeOnGeneratorCall() { + return driver.call(function* () { + var x = yield webdriver.promise.fulfilled(1); + return this.name + x; + }, {name: 'Bob'}).then(function(value) { + assertEquals('Bob1', value); + }); +} + + +function testCanSpecifyArgsOnGeneratorCall() { + return driver.call(function* (a, b) { + var x = yield webdriver.promise.fulfilled(1); + var y = yield webdriver.promise.fulfilled(2); + return [x + y, a, b]; + }, null, 'abc', 123).then(function(value) { + assertArrayEquals([3, 'abc', 123], value); + }); +} + + +function testCanUseGeneratorWithWebDriverWait() { + var values = []; + return driver.wait(function* () { + yield values.push(1); + values.push(yield webdriver.promise.delayed(10).then(function() { + return 2; + })); + yield values.push(3); + return values.length === 6; + }, 250).then(function() { + assertArrayEquals([1, 2, 3, 1, 2, 3], values); + }); +} + + +/** + * @constructor + * @implements {webdriver.CommandExecutor} + */ +function ExplodingExecutor() {} + + +/** @override */ +ExplodingExecutor.prototype.execute = function(command, cb) { + cb(Error('Unsupported operation')); +}; diff --git a/lib/webdriver/test/webdriver_test.js b/lib/webdriver/test/webdriver_test.js new file mode 100644 index 0000000..7602f60 --- /dev/null +++ b/lib/webdriver/test/webdriver_test.js @@ -0,0 +1,2369 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.require('bot.ErrorCode'); +goog.require('goog.Promise'); +goog.require('goog.functions'); +goog.require('goog.testing.PropertyReplacer'); +goog.require('goog.testing.MockControl'); +goog.require('goog.testing.jsunit'); +goog.require('goog.userAgent'); +goog.require('webdriver.Capabilities'); +goog.require('webdriver.Command'); +goog.require('webdriver.CommandExecutor'); +goog.require('webdriver.CommandName'); +goog.require('webdriver.FileDetector'); +goog.require('webdriver.WebDriver'); +goog.require('webdriver.Serializable'); +goog.require('webdriver.Session'); +goog.require('webdriver.logging'); +goog.require('webdriver.promise'); +goog.require('webdriver.test.testutil'); + +var SESSION_ID = 'test_session_id'; + +var STUB_DRIVER = { + controlFlow: goog.nullFunction +}; + +// Alias some long names that interfere with test readability. +var CName = webdriver.CommandName, + ECode = bot.ErrorCode, + StubError = webdriver.test.testutil.StubError, + throwStubError = webdriver.test.testutil.throwStubError, + assertIsStubError = webdriver.test.testutil.assertIsStubError; + +// By is exported by webdriver.By, but IDEs don't recognize +// goog.exportSymbol. Explicitly define it here to make the +// IDE stop complaining. +var By = webdriver.By; + +var driver; +var flow; +var mockControl; +var uncaughtExceptions; + +function shouldRunTests() { + return !goog.userAgent.IE || goog.userAgent.isVersionOrHigher(10); +} + + +function setUp() { + mockControl = new goog.testing.MockControl(); + flow = webdriver.promise.controlFlow(); + uncaughtExceptions = []; + flow.on('uncaughtException', onUncaughtException); +} + + +function tearDown() { + return waitForIdle(flow).then(function() { + mockControl.$verifyAll(); + mockControl.$tearDown(); + + assertArrayEquals('There were uncaught exceptions', + [], uncaughtExceptions); + flow.reset(); + }); +} + + +function onUncaughtException(e) { + uncaughtExceptions.push(e); +} + + +function waitForIdle(opt_flow) { + var theFlow = opt_flow || flow; + return new goog.Promise(function(fulfill, reject) { + if (theFlow.isIdle()) { + fulfill(); + return; + } + theFlow.once('idle', fulfill); + theFlow.once('uncaughtException', reject); + }); +} + + +function waitForAbort(opt_flow, opt_n) { + var n = opt_n || 1; + var theFlow = opt_flow || flow; + theFlow.removeAllListeners( + webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); + return new goog.Promise(function(fulfill, reject) { + theFlow.once('idle', function() { + reject(Error('expected flow to report an unhandled error')); + }); + + var errors = []; + theFlow.on('uncaughtException', onError); + function onError(e) { + errors.push(e); + if (errors.length === n) { + theFlow.removeListener('uncaughtException', onError); + fulfill(n === 1 ? errors[0] : errors); + } + } + }); +} + + +function expectedError(code, message) { + return function(e) { + assertEquals('Wrong error message', message, e.message); + assertEquals('Wrong error code', code, e.code); + }; +} + + +function createCommandMatcher(commandName, parameters) { + return new goog.testing.mockmatchers.ArgumentMatcher(function(actual) { + assertEquals('wrong name', commandName, actual.getName()); + var differences = goog.testing.asserts.findDifferences( + parameters, actual.getParameters()); + assertNull( + 'Wrong parameters for "' + commandName + '"' + + '\n Expected: ' + JSON.stringify(parameters) + + '\n Actual: ' + JSON.stringify(actual.getParameters()), + differences); + return true; + }, commandName + '(' + JSON.stringify(parameters) + ')'); +} + + +TestHelper = function() { + this.executor = mockControl.createStrictMock(webdriver.CommandExecutor); +}; + + +TestHelper.prototype.expect = function(commandName, opt_parameters) { + return new TestHelper.Command(this, commandName, opt_parameters); +}; + + +TestHelper.prototype.replayAll = function() { + mockControl.$replayAll(); + return this; +}; + + +TestHelper.Command = function(testHelper, commandName, opt_parameters) { + this.helper_ = testHelper; + this.name_ = commandName; + this.toDo_ = null; + this.anyTimes_ = false; + this.times_ = 0; + this.sessionId_ = SESSION_ID; + this.withParameters(opt_parameters || {}); +}; + + +TestHelper.Command.prototype.withParameters = function(parameters) { + this.parameters_ = parameters; + if (this.name_ !== CName.NEW_SESSION) { + this.parameters_['sessionId'] = this.sessionId_; + } + return this; +}; + + +TestHelper.Command.prototype.buildExpectation_ = function() { + var commandMatcher = createCommandMatcher(this.name_, this.parameters_); + assertNotNull(this.toDo_); + var expectation = this.helper_.executor. + execute(commandMatcher, goog.testing.mockmatchers.isFunction). + $does(this.toDo_); + if (this.anyTimes_) { + assertEquals(0, this.times_); + expectation.$anyTimes(); + } + if (this.times_) { + assertFalse(this.anyTimes_); + expectation.$times(this.times_); + } +}; + + +TestHelper.Command.prototype.andReturn = function(code, opt_value) { + this.toDo_ = function(command, callback) { + callback(null, { + 'status': code, + 'sessionId': { + 'value': SESSION_ID + }, + 'value': goog.isDef(opt_value) ? opt_value : null + }); + }; + return this; +}; + + +TestHelper.Command.prototype.anyTimes = function() { + this.anyTimes_ = true; + return this; +}; + + +TestHelper.Command.prototype.times = function(n) { + this.times_ = n; + return this; +}; + + +TestHelper.Command.prototype.andReturnSuccess = function(opt_returnValue) { + return this.andReturn(ECode.SUCCESS, opt_returnValue); +}; + + +TestHelper.Command.prototype.andReturnError = function(errCode, opt_value) { + return this.andReturn(errCode, opt_value); +}; + + +TestHelper.Command.prototype.replayAll = function() { + if (!this.toDo_) { + this.andReturnSuccess(null); + } + this.buildExpectation_(); + return this.helper_.replayAll(); +}; + + +TestHelper.Command.prototype.expect = function(name, opt_parameters) { + if (!this.toDo_) { + this.andReturnSuccess(null); + } + this.buildExpectation_(); + return this.helper_.expect(name, opt_parameters); +}; + + +/** + * @param {!(webdriver.Session|webdriver.promise.Promise)=} opt_session The + * session to use. + * @return {!webdriver.WebDriver} A new driver instance. + */ +TestHelper.prototype.createDriver = function(opt_session) { + var session = opt_session || new webdriver.Session(SESSION_ID, {}); + return new webdriver.WebDriver(session, this.executor); +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// Tests +// +////////////////////////////////////////////////////////////////////////////// + +function testAttachToSession_sessionIsAvailable() { + var testHelper = new TestHelper(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnSuccess({'browserName': 'firefox'}). + replayAll(); + + var driver = webdriver.WebDriver.attachToSession(testHelper.executor, + SESSION_ID); + return driver.getSession().then(function(session) { + webdriver.test.testutil.assertObjectEquals({ + 'value':'test_session_id' + }, session.getId()); + assertEquals('firefox', session.getCapability('browserName')); + }); +} + + +function testAttachToSession_failsToGetSessionInfo() { + var testHelper = new TestHelper(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnError(ECode.UNKNOWN_ERROR, {'message': 'boom'}). + replayAll(); + + var driver = webdriver.WebDriver.attachToSession(testHelper.executor, + SESSION_ID); + return driver.getSession().then(fail, function(e) { + assertEquals(bot.ErrorCode.UNKNOWN_ERROR, e.code); + assertEquals('boom', e.message); + }); +} + + +function testAttachToSession_usesActiveFlowByDefault() { + var testHelper = new TestHelper(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnSuccess({}). + replayAll(); + + var driver = webdriver.WebDriver.attachToSession(testHelper.executor, + SESSION_ID); + assertEquals(driver.controlFlow(), webdriver.promise.controlFlow()); + + return waitForIdle(driver.controlFlow()); +} + + +function testAttachToSession_canAttachInCustomFlow() { + var testHelper = new TestHelper(). + expect(CName.DESCRIBE_SESSION). + withParameters({'sessionId': SESSION_ID}). + andReturnSuccess({}). + replayAll(); + + var otherFlow = new webdriver.promise.ControlFlow(); + var driver = webdriver.WebDriver.attachToSession( + testHelper.executor, SESSION_ID, otherFlow); + assertEquals(otherFlow, driver.controlFlow()); + assertNotEquals(otherFlow, webdriver.promise.controlFlow()); + + return waitForIdle(otherFlow); +} + + +function testCreateSession_happyPathWithCapabilitiesHashObject() { + var testHelper = new TestHelper(). + expect(CName.NEW_SESSION). + withParameters({ + 'desiredCapabilities': {'browserName': 'firefox'} + }). + andReturnSuccess({'browserName': 'firefox'}). + replayAll(); + + var driver = webdriver.WebDriver.createSession(testHelper.executor, { + 'browserName': 'firefox' + }); + return driver.getSession().then(function(session) { + webdriver.test.testutil.assertObjectEquals({ + 'value':'test_session_id' + }, session.getId()); + assertEquals('firefox', session.getCapability('browserName')); + }); +} + + +function testCreateSession_happyPathWithCapabilitiesInstance() { + var testHelper = new TestHelper(). + expect(CName.NEW_SESSION). + withParameters({ + 'desiredCapabilities': {'browserName': 'firefox'} + }). + andReturnSuccess({'browserName': 'firefox'}). + replayAll(); + + var driver = webdriver.WebDriver.createSession( + testHelper.executor, webdriver.Capabilities.firefox()); + return driver.getSession().then(function(session) { + webdriver.test.testutil.assertObjectEquals({ + 'value':'test_session_id' + }, session.getId()); + assertEquals('firefox', session.getCapability('browserName')); + }); +} + + +function testCreateSession_failsToCreateSession() { + var testHelper = new TestHelper(). + expect(CName.NEW_SESSION). + withParameters({ + 'desiredCapabilities': {'browserName': 'firefox'} + }). + andReturnError(ECode.UNKNOWN_ERROR, {'message': 'boom'}). + replayAll(); + + var driver = webdriver.WebDriver.createSession(testHelper.executor, { + 'browserName': 'firefox' + }); + return driver.getSession().then(fail, function(e) { + assertEquals(bot.ErrorCode.UNKNOWN_ERROR, e.code); + assertEquals('boom', e.message); + }); +} + + +function testCreateSession_usesActiveFlowByDefault() { + var testHelper = new TestHelper(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': {}}). + andReturnSuccess({}). + replayAll(); + + var driver = webdriver.WebDriver.createSession(testHelper.executor, {}); + assertEquals(webdriver.promise.controlFlow(), driver.controlFlow()); + + return waitForIdle(driver.controlFlow()); +} + + +function testCreateSession_canCreateInCustomFlow() { + var testHelper = new TestHelper(). + expect(CName.NEW_SESSION). + withParameters({'desiredCapabilities': {}}). + andReturnSuccess({}). + replayAll(); + + var otherFlow = new webdriver.promise.ControlFlow(goog.global); + var driver = webdriver.WebDriver.createSession( + testHelper.executor, {}, otherFlow); + assertEquals(otherFlow, driver.controlFlow()); + assertNotEquals(otherFlow, webdriver.promise.controlFlow()); + + return waitForIdle(otherFlow); +} + + +function testToWireValue_function() { + var fn = function() { return 'foo'; }; + return webdriver.WebDriver.toWireValue_(fn).then(function(value) { + assertEquals(fn + '', value); + }); +} + + +function testToWireValue_date() { + if (goog.userAgent.IE) { + return; // Because IE... + } + return webdriver.WebDriver.toWireValue_(new Date(605728511546)). + then(function(value) { + assertEquals('1989-03-12T17:55:11.546Z', value); + }); +} + + +function testToWireValue_simpleObject() { + var expected = {'sessionId': 'foo'}; + return webdriver.WebDriver.toWireValue_({ + 'sessionId': new webdriver.Session('foo', {}) + }).then(function(actual) { + webdriver.test.testutil.assertObjectEquals(expected, actual); + }); +} + + +function testToWireValue_nestedObject() { + var expected = {'sessionId': {'value': 'foo'}}; + return webdriver.WebDriver.toWireValue_({ + 'sessionId': { + 'value': new webdriver.Session('foo', {}) + } + }).then(function(actual) { + webdriver.test.testutil.assertObjectEquals(expected, actual); + }); +} + + +function testToWireValue_capabilities() { + var prefs = new webdriver.logging.Preferences(); + prefs.setLevel(webdriver.logging.Type.BROWSER, + webdriver.logging.Level.DEBUG); + + var caps = webdriver.Capabilities.chrome(); + caps.set(webdriver.Capability.LOGGING_PREFS, prefs); + + return webdriver.WebDriver.toWireValue_(caps).then(function(actual) { + webdriver.test.testutil.assertObjectEquals({ + 'browserName': 'chrome', + 'loggingPrefs': { + 'browser': 'DEBUG' + } + }, actual); + }); +} + + +function testToWireValue_webElement() { + var expected = {}; + expected[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + + var element = new webdriver.WebElement(STUB_DRIVER, expected); + return webdriver.WebDriver.toWireValue_(element).then(function(actual) { + webdriver.test.testutil.assertObjectEquals(expected, actual); + }); +} + + +function testToWireValue_webElementPromise() { + var expected = {}; + expected[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + + var element = new webdriver.WebElement(STUB_DRIVER, expected); + var elementPromise = new webdriver.WebElementPromise(STUB_DRIVER, + webdriver.promise.fulfilled(element)); + return webdriver.WebDriver.toWireValue_(elementPromise). + then(function(actual) { + webdriver.test.testutil.assertObjectEquals(expected, actual); + }); +} + + +function testToWireValue_domElement() { + assertThrows( + goog.partial(webdriver.WebDriver.toWireValue_, document.body)); +} + + +function testToWireValue_serializableObject() { + /** + * @constructor + * @extends {webdriver.Serializable} + */ + var CustomSerializable = function () { + webdriver.Serializable.call(this); + }; + goog.inherits(CustomSerializable, webdriver.Serializable); + + /** @override */ + CustomSerializable.prototype.serialize = function() { + return webdriver.promise.fulfilled({ + name: webdriver.promise.fulfilled('bob'), + age: 30 + }); + }; + + var obj = new CustomSerializable(); + return webdriver.WebDriver.toWireValue_(obj). + then(function(actual) { + webdriver.test.testutil.assertObjectEquals( + {name: 'bob', age: 30}, actual); + }); +} + + +function testToWireValue_simpleArray() { + var expected = ['foo']; + return webdriver.WebDriver.toWireValue_([new webdriver.Session('foo', {})]). + then(function(actual) { + assertArrayEquals(expected, actual); + }); +} + + +function testToWireValue_arrayWithWebElement() { + var elementJson = {}; + elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + + var element = new webdriver.WebElement(STUB_DRIVER, elementJson); + return webdriver.WebDriver.toWireValue_([element]). + then(function(actual) { + assertTrue(goog.isArray(actual)); + assertEquals(1, actual.length); + webdriver.test.testutil.assertObjectEquals(elementJson, actual[0]); + }); +} + + +function testToWireValue_arrayWithWebElementPromise() { + var elementJson = {}; + elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + + var element = new webdriver.WebElement(STUB_DRIVER, elementJson); + var elementPromise = new webdriver.WebElementPromise(STUB_DRIVER, + webdriver.promise.fulfilled(element)); + + return webdriver.WebDriver.toWireValue_([elementPromise]). + then(function(actual) { + assertTrue(goog.isArray(actual)); + assertEquals(1, actual.length); + webdriver.test.testutil.assertObjectEquals(elementJson, actual[0]); + }); +} + + +function testToWireValue_complexArray() { + var elementJson = {}; + elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + var expected = ['abc', 123, true, elementJson, [123, {'foo': 'bar'}]]; + + var element = new webdriver.WebElement(STUB_DRIVER, elementJson); + var input = ['abc', 123, true, element, [123, {'foo': 'bar'}]]; + return webdriver.WebDriver.toWireValue_(input). + then(function(actual) { + webdriver.test.testutil.assertObjectEquals(expected, actual); + }); +} + + +function testToWireValue_arrayWithNestedPromises() { + return webdriver.WebDriver.toWireValue_([ + 'abc', + webdriver.promise.fulfilled([ + 123, + webdriver.promise.fulfilled(true) + ]) + ]).then(function(actual) { + assertEquals(2, actual.length); + assertEquals('abc', actual[0]); + assertArrayEquals([123, true], actual[1]); + }); +} + + +function testToWireValue_complexHash() { + var elementJson = {}; + elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + var expected = { + 'script': 'return 1', + 'args': ['abc', 123, true, elementJson, [123, {'foo': 'bar'}]], + 'sessionId': 'foo' + }; + + var element = new webdriver.WebElement(STUB_DRIVER, elementJson); + var parameters = { + 'script': 'return 1', + 'args':['abc', 123, true, element, [123, {'foo': 'bar'}]], + 'sessionId': new webdriver.Session('foo', {}) + }; + + return webdriver.WebDriver.toWireValue_(parameters). + then(function(actual) { + webdriver.test.testutil.assertObjectEquals(expected, actual); + }); +} + + +function testFromWireValue_primitives() { + assertEquals(1, webdriver.WebDriver.fromWireValue_({}, 1)); + assertEquals('', webdriver.WebDriver.fromWireValue_({}, '')); + assertEquals(true, webdriver.WebDriver.fromWireValue_({}, true)); + + assertUndefined(webdriver.WebDriver.fromWireValue_({}, undefined)); + assertNull(webdriver.WebDriver.fromWireValue_({}, null)); +} + + +function testFromWireValue_webElements() { + var json = {}; + json[webdriver.WebElement.ELEMENT_KEY] = 'foo'; + + var element = webdriver.WebDriver.fromWireValue_(STUB_DRIVER, json); + assertEquals(STUB_DRIVER, element.getDriver()); + + return element.getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json, id); + }); +} + + +function testFromWireValue_simpleObject() { + var json = {'sessionId': 'foo'}; + var out = webdriver.WebDriver.fromWireValue_({}, json); + webdriver.test.testutil.assertObjectEquals(json, out); +} + + +function testFromWireValue_nestedObject() { + var json = {'foo': {'bar': 123}}; + var out = webdriver.WebDriver.fromWireValue_({}, json); + webdriver.test.testutil.assertObjectEquals(json, out); +} + + +function testFromWireValue_array() { + var json = [{'foo': {'bar': 123}}]; + var out = webdriver.WebDriver.fromWireValue_({}, json); + webdriver.test.testutil.assertObjectEquals(json, out); +} + + +function testFromWireValue_passesThroughFunctionProperties() { + var json = [{'foo': {'bar': 123}, 'func': goog.nullFunction}]; + var out = webdriver.WebDriver.fromWireValue_({}, json); + webdriver.test.testutil.assertObjectEquals(json, out); +} + + +function testDoesNotExecuteCommandIfSessionDoesNotResolve() { + var session = webdriver.promise.rejected(new StubError); + var testHelper = new TestHelper().replayAll(); + testHelper.createDriver(session).getTitle(); + return waitForAbort().then(assertIsStubError); +} + + +function testCommandReturnValuesArePassedToFirstCallback() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE).andReturnSuccess('Google Search'). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.getTitle().then(function(title) { + assertEquals('Google Search', title); + }); +} + + +function testStopsCommandExecutionWhenAnErrorOccurs() { + var testHelper = new TestHelper(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({'name': 'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). + replayAll(); + + var driver = testHelper.createDriver(); + driver.switchTo().window('foo'); + driver.getTitle(); // mock should blow if this gets executed + + return waitForAbort(). + then(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')); +} + + +function testCanSuppressCommandFailures() { + var testHelper = new TestHelper(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({'name': 'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). + expect(CName.GET_TITLE). + andReturnSuccess('Google Search'). + replayAll(); + + var driver = testHelper.createDriver(); + driver.switchTo().window('foo').thenCatch(function(e) { + assertEquals(ECode.NO_SUCH_WINDOW, e.code); + assertEquals('window not found', e.message); + }); + driver.getTitle(); + return waitForIdle(); +} + + +function testErrorsPropagateUpToTheRunningApplication() { + var testHelper = new TestHelper(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message': 'window not found'}). + replayAll(); + + testHelper.createDriver().switchTo().window('foo'); + return waitForAbort(). + then(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')); +} + + +function testErrbacksThatReturnErrorsStillSwitchToCallbackChain() { + var testHelper = new TestHelper(). + expect(CName.SWITCH_TO_WINDOW). + withParameters({'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.switchTo().window('foo'). + thenCatch(function() { return new StubError; }); + then(assertIsStubError); +} + + +function testErrbacksThrownCanOverrideOriginalError() { + var testHelper = new TestHelper(). + expect(CName.SWITCH_TO_WINDOW, {'name': 'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + replayAll(); + + var driver = testHelper.createDriver(); + driver.switchTo().window('foo').thenCatch(throwStubError); + + return waitForAbort().then(assertIsStubError); +} + + +function testCannotScheduleCommandsIfTheSessionIdHasBeenDeleted() { + var testHelper = new TestHelper().replayAll(); + var driver = testHelper.createDriver(); + delete driver.session_; + assertThrows(goog.bind(driver.get, driver, 'http://www.google.com')); +} + + +function testDeletesSessionIdAfterQuitting() { + var driver; + var testHelper = new TestHelper(). + expect(CName.QUIT). + replayAll(); + + driver = testHelper.createDriver(); + return driver.quit().then(function() { + assertUndefined('Session ID should have been deleted', driver.session_); + }); +} + + +function testReportsErrorWhenExecutingCommandsAfterExecutingAQuit() { + var testHelper = new TestHelper(). + expect(CName.QUIT). + replayAll(); + + var driver = testHelper.createDriver(); + driver.quit(); + driver.get('http://www.google.com'); + return waitForAbort(). + then(expectedError(undefined, + 'This driver instance does not have a valid session ID ' + + '(did you call WebDriver.quit()?) and may no longer be used.')); +} + + +function testCallbackCommandsExecuteBeforeNextCommand() { + var testHelper = new TestHelper(). + expect(CName.GET_CURRENT_URL). + expect(CName.GET, {'url': 'http://www.google.com'}). + expect(CName.CLOSE). + expect(CName.GET_TITLE). + replayAll(); + + var driver = testHelper.createDriver(); + driver.getCurrentUrl().then(function() { + driver.get('http://www.google.com').then(function() { + driver.close(); + }); + }); + driver.getTitle(); + + return waitForIdle(); +} + + +function testEachCallbackFrameRunsToCompletionBeforeTheNext() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.GET_CURRENT_URL). + expect(CName.GET_CURRENT_WINDOW_HANDLE). + expect(CName.CLOSE). + expect(CName.QUIT). + replayAll(); + + var driver = testHelper.createDriver(); + driver.getTitle(). + // Everything in this callback... + then(function() { + driver.getCurrentUrl(); + driver.getWindowHandle(); + }). + // ...should execute before everything in this callback. + then(function() { + driver.close(); + }); + // This should execute after everything above + driver.quit(); + + return waitForIdle(); +} + + +function testNestedCommandFailuresBubbleUpToGlobalHandlerIfUnsuppressed() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.SWITCH_TO_WINDOW, {'name': 'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + replayAll(); + + var driver = testHelper.createDriver(); + driver.getTitle().then(function() { + driver.switchTo().window('foo'); + }); + + return waitForAbort(). + then(expectedError(ECode.NO_SUCH_WINDOW, 'window not found')); +} + + +function testNestedCommandFailuresCanBeSuppressWhenTheyOccur() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.SWITCH_TO_WINDOW, {'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + expect(CName.CLOSE). + replayAll(); + + var driver = testHelper.createDriver(); + driver.getTitle().then(function() { + driver.switchTo().window('foo').thenCatch(goog.nullFunction); + }); + driver.close(); + + return waitForIdle(); +} + + +function testNestedCommandFailuresBubbleUpThroughTheFrameStack() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.SWITCH_TO_WINDOW, {'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + replayAll(); + + var driver = testHelper.createDriver(); + driver.getTitle(). + then(function() { + return driver.switchTo().window('foo'); + }). + thenCatch(function(e) { + assertEquals(ECode.NO_SUCH_WINDOW, e.code); + assertEquals('window not found', e.message); + }); + + return waitForIdle(); +} + + +function testNestedCommandFailuresCanBeCaughtAndSuppressed() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.GET_CURRENT_URL). + expect(CName.SWITCH_TO_WINDOW, {'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + expect(CName.CLOSE). + replayAll(); + + var driver = testHelper.createDriver(); + driver.getTitle().then(function() { + driver.getCurrentUrl(). + then(function() { + return driver.switchTo().window('foo'); + }). + thenCatch(goog.nullFunction); + driver.close(); + }); + + return waitForIdle(); +} + + +function testReturningADeferredResultFromACallback() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.GET_CURRENT_URL). + andReturnSuccess('http://www.google.com'). + replayAll(); + + var driver = testHelper.createDriver(); + driver.getTitle(). + then(function() { + return driver.getCurrentUrl(); + }). + then(function(value) { + assertEquals('http://www.google.com', value); + }); + return waitForIdle(); +} + + +function testReturningADeferredResultFromAnErrbackSuppressesTheError() { + var count = 0; + var testHelper = new TestHelper(). + expect(CName.SWITCH_TO_WINDOW, {'name':'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + expect(CName.GET_CURRENT_URL). + andReturnSuccess('http://www.google.com'). + replayAll(); + + var driver = testHelper.createDriver(); + driver.switchTo().window('foo'). + thenCatch(function(e) { + assertEquals(ECode.NO_SUCH_WINDOW, e.code); + assertEquals('window not found', e.message); + count += 1; + return driver.getCurrentUrl(); + }). + then(function(url) { + count += 1; + assertEquals('http://www.google.com', url); + }); + return waitForIdle().then(function() { + assertEquals(2, count); + }); +} + + +function testExecutingACustomFunctionThatReturnsANonDeferred() { + var testHelper = new TestHelper().replayAll(); + + var driver = testHelper.createDriver(); + return driver.call(goog.functions.constant('abc123')).then(function(value) { + assertEquals('abc123', value); + }); +} + + +function testExecutionOrderwithCustomFunctions() { + var msg = []; + var testHelper = new TestHelper(). + expect(CName.GET_TITLE).andReturnSuccess('cheese '). + expect(CName.GET_CURRENT_URL).andReturnSuccess('tasty'). + replayAll(); + + var driver = testHelper.createDriver(); + + var pushMsg = goog.bind(msg.push, msg); + driver.getTitle().then(pushMsg); + driver.call(goog.functions.constant('is ')).then(pushMsg); + driver.getCurrentUrl().then(pushMsg); + driver.call(goog.functions.constant('!')).then(pushMsg); + + return waitForIdle().then(function() { + assertEquals('cheese is tasty!', msg.join('')); + }); +} + + +function testPassingArgumentsToACustomFunction() { + var testHelper = new TestHelper().replayAll(); + + var add = function(a, b) { + return a + b; + }; + var driver = testHelper.createDriver(); + return driver.call(add, null, 1, 2).then(function(value) { + assertEquals(3, value); + }); +} + +function testPassingPromisedArgumentsToACustomFunction() { + var testHelper = new TestHelper().replayAll(); + + var promisedArg = webdriver.promise.fulfilled(2); + var add = function(a, b) { + return a + b; + }; + var driver = testHelper.createDriver(); + return driver.call(add, null, 1, promisedArg).then(function(value) { + assertEquals(3, value); + }); +} + +function testPassingArgumentsAndScopeToACustomFunction() { + function Foo(name) { + this.name = name; + } + Foo.prototype.getName = function() { + return this.name; + }; + var foo = new Foo('foo'); + + var testHelper = new TestHelper().replayAll(); + var driver = testHelper.createDriver(); + return driver.call(foo.getName, foo).then(function(value) { + assertEquals('foo', value); + }); +} + + +function testExecutingACustomFunctionThatThrowsAnError() { + var testHelper = new TestHelper().replayAll(); + var driver = testHelper.createDriver(); + return driver.call(goog.functions.error('bam!')).then(fail, function(e) { + assertTrue(e instanceof Error); + assertEquals('bam!', e.message); + }); +} + + +function testExecutingACustomFunctionThatSchedulesCommands() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.CLOSE). + expect(CName.QUIT). + replayAll(); + + var driver = testHelper.createDriver(); + driver.call(function() { + driver.getTitle(); + driver.close(); + }); + driver.quit(); + return waitForIdle(); +} + + +function testExecutingAFunctionThatReturnsATaskResultAfterSchedulingAnother() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + andReturnSuccess('Google Search'). + expect(CName.CLOSE). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.call(function() { + var title = driver.getTitle(); + driver.close(); + return title; + }).then(function(title) { + assertEquals('Google Search', title); + }); +} + + +function testExecutingACustomFunctionWhoseNestedCommandFails() { + var testHelper = new TestHelper(). + expect(CName.SWITCH_TO_WINDOW, {'name': 'foo'}). + andReturnError(ECode.NO_SUCH_WINDOW, {'message':'window not found'}). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.call(function() { + return driver.switchTo().window('foo'); + }).then(fail, function(e) { + assertEquals(ECode.NO_SUCH_WINDOW, e.code); + assertEquals('window not found', e.message); + }); +} + + +function testCustomFunctionDoesNotCompleteUntilReturnedPromiseIsResolved() { + var testHelper = new TestHelper().replayAll(); + + var order = []; + var driver = testHelper.createDriver(); + + var d = webdriver.promise.defer(); + d.promise.then(function() { + order.push('b'); + }); + + driver.call(function() { + order.push('a'); + return d.promise; + }); + driver.call(function() { + order.push('c'); + }); + + // timeout to ensure the first function starts its execution before we + // trigger d's callbacks. + webdriver.promise.delayed(0).then(function() { + assertArrayEquals(['a'], order); + d.fulfill(); + }); + return waitForIdle().then(function() { + assertArrayEquals(['a', 'b', 'c'], order); + }); +} + + +function testNestedFunctionCommandExecutionOrder() { + var msg = []; + var testHelper = new TestHelper().replayAll(); + + var driver = testHelper.createDriver(); + driver.call(msg.push, msg, 'a'); + driver.call(function() { + driver.call(msg.push, msg, 'c'); + driver.call(function() { + driver.call(msg.push, msg, 'e'); + driver.call(msg.push, msg, 'f'); + }); + driver.call(msg.push, msg, 'd'); + }); + driver.call(msg.push, msg, 'b'); + return waitForIdle().then(function() { + assertEquals('acefdb', msg.join('')); + }); +} + + +function testExecutingNestedFunctionCommands() { + var msg = []; + var testHelper = new TestHelper().replayAll(); + var driver = testHelper.createDriver(); + var pushMsg = goog.bind(msg.push, msg); + driver.call(goog.functions.constant('cheese ')).then(pushMsg); + driver.call(function() { + driver.call(goog.functions.constant('is ')).then(pushMsg); + driver.call(goog.functions.constant('tasty')).then(pushMsg); + }); + driver.call(goog.functions.constant('!')).then(pushMsg); + return waitForIdle().then(function() { + assertEquals('cheese is tasty!', msg.join('')); + }); +} + + +function testReturnValuesFromNestedFunctionCommands() { + var testHelper = new TestHelper().replayAll(); + var driver = testHelper.createDriver(); + return driver.call(function() { + return driver.call(function() { + return driver.call(goog.functions.constant('foobar')); + }); + }).then(function(value) { + assertEquals('foobar', value); + }); +} + + +function testExecutingANormalCommandAfterNestedCommandsThatReturnsAnAction() { + var msg = []; + var testHelper = new TestHelper(). + expect(CName.CLOSE). + replayAll(); + var driver = testHelper.createDriver(); + driver.call(function() { + return driver.call(function() { + msg.push('a'); + return driver.call(goog.functions.constant('foobar')); + }); + }); + driver.close().then(function() { + msg.push('b'); + }); + return waitForIdle().then(function() { + assertEquals('ab', msg.join('')); + }); +} + + +function testNestedCommandErrorsBubbleUp_caught() { + var testHelper = new TestHelper().replayAll(); + var driver = testHelper.createDriver(); + var result = driver.call(function() { + return driver.call(function() { + return driver.call(goog.functions.error('bam!')); + }); + }).then(fail, expectedError(undefined, 'bam!')); + return goog.Promise.all([waitForIdle(), result]); +} + + +function testNestedCommandErrorsBubbleUp_uncaught() { + var testHelper = new TestHelper().replayAll(); + var driver = testHelper.createDriver(); + driver.call(function() { + return driver.call(function() { + return driver.call(goog.functions.error('bam!')); + }); + }); + return waitForAbort().then(expectedError(undefined, 'bam!')); +} + + +function testExecutingNestedCustomFunctionsThatSchedulesCommands() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE). + expect(CName.CLOSE). + replayAll(); + + var driver = testHelper.createDriver(); + driver.call(function() { + driver.call(function() { + driver.getTitle(); + }); + driver.close(); + }); + return waitForIdle(); +} + + +function testExecutingACustomFunctionThatReturnsADeferredAction() { + var testHelper = new TestHelper(). + expect(CName.GET_TITLE).andReturnSuccess('Google'). + replayAll(); + + var driver = testHelper.createDriver(); + driver.call(function() { + return driver.getTitle(); + }).then(function(title) { + assertEquals('Google', title); + }); + return waitForIdle(); +} + +function testWebElementPromise_resolvesWhenUnderlyingElementDoes() { + var el = new webdriver.WebElement(STUB_DRIVER, {'ELEMENT': 'foo'}); + var promise = webdriver.promise.fulfilled(el); + return new webdriver.WebElementPromise(STUB_DRIVER, promise). + then(function(e) { + assertEquals(e, el); + }); +} + +function testWebElement_resolvesBeforeCallbacksOnWireValueTrigger() { + var el = new webdriver.promise.Deferred(); + + var element = new webdriver.WebElementPromise(STUB_DRIVER, el.promise); + var messages = []; + + element.then(function() { + messages.push('element resolved'); + }); + element.getId().then(function() { + messages.push('wire value resolved'); + }); + + assertArrayEquals([], messages); + el.fulfill(new webdriver.WebElement(STUB_DRIVER, {'ELEMENT': 'foo'})); + return waitForIdle().then(function() { + assertArrayEquals([ + 'element resolved', + 'wire value resolved' + ], messages); + }); +} + +function testWebElement_isRejectedIfUnderlyingIdIsRejected() { + var element = new webdriver.WebElementPromise( + STUB_DRIVER, webdriver.promise.rejected(new StubError)); + return element.then(fail, assertIsStubError); +} + + +function testExecuteScript_nullReturnValue() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(null). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.executeScript('return document.body;').then(function(result) { + assertNull(result); + }); +} + + +function testExecuteScript_primitiveReturnValue() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(123). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.executeScript('return document.body;').then(function(result) { + assertEquals(123, result); + }); +} + + +function testExecuteScript_webElementReturnValue() { + var json = {}; + json[webdriver.WebElement.ELEMENT_KEY] = 'foo'; + + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(json). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.executeScript('return document.body;'). + then(function(webelement) { + return webdriver.promise.when(webelement.id_, function(id) { + webdriver.test.testutil.assertObjectEquals(id, json); + }); + }); +} + + +function testExecuteScript_arrayReturnValue() { + var json = [{}]; + json[0][webdriver.WebElement.ELEMENT_KEY] = 'foo'; + + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(json). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.executeScript('return document.body;'). + then(function(array) { + return webdriver.promise.when(array[0].id_, function(id) { + webdriver.test.testutil.assertObjectEquals(id, json[0]); + }); + }); +} + + +function testExecuteScript_objectReturnValue() { + var json = {'foo':{}}; + json['foo'][webdriver.WebElement.ELEMENT_KEY] = 'foo'; + + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return document.body;', + 'args': [] + }). + andReturnSuccess(json). + replayAll(); + + var driver = testHelper.createDriver(); + var callback; + return driver.executeScript('return document.body;'). + then(function(obj) { + return webdriver.promise.when(obj['foo'].id_, function(id) { + webdriver.test.testutil.assertObjectEquals(id, json['foo']); + }); + }); +} + + +function testExecuteScript_scriptAsFunction() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return (' + goog.nullFunction + + ').apply(null, arguments);', + 'args': [] + }). + andReturnSuccess(null). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.executeScript(goog.nullFunction); +} + + +function testExecuteScript_simpleArgumentConversion() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': ['abc', 123, true, [123, {'foo': 'bar'}]] + }). + andReturnSuccess(null). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.executeScript( + 'return 1;', 'abc', 123, true, [123, {'foo': 'bar'}]); +} + + +function testExecuteScript_webElementArgumentConversion() { + var elementJson = {}; + elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': [elementJson] + }). + andReturnSuccess(null). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.executeScript('return 1;', + new webdriver.WebElement(driver, elementJson)); +} + + +function testExecuteScript_webElementPromiseArgumentConversion() { + var elementJson = {'ELEMENT':'bar'}; + + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnSuccess(elementJson). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': [elementJson] + }). + andReturnSuccess(null). + replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(By.id('foo')); + return driver.executeScript('return 1;', element); +} + + +function testExecuteScript_argumentConversion() { + var elementJson = {}; + elementJson[webdriver.WebElement.ELEMENT_KEY] = 'fefifofum'; + + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'return 1;', + 'args': ['abc', 123, true, elementJson, [123, {'foo': 'bar'}]] + }). + andReturnSuccess(null). + replayAll(); + + var driver = testHelper.createDriver(); + var element = new webdriver.WebElement(driver, elementJson); + return driver.executeScript('return 1;', + 'abc', 123, true, element, [123, {'foo': 'bar'}]); +} + + +function testExecuteScript_scriptReturnsAnError() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT). + withParameters({ + 'script': 'throw Error(arguments[0]);', + 'args': ['bam'] + }). + andReturnError(ECode.UNKNOWN_ERROR, {'message':'bam'}). + replayAll(); + var driver = testHelper.createDriver(); + return driver.executeScript('throw Error(arguments[0]);', 'bam'). + then(fail, expectedError(ECode.UNKNOWN_ERROR, 'bam')); +} + + +function testExecuteScript_failsIfArgumentIsARejectedPromise() { + var testHelper = new TestHelper().replayAll(); + + var arg = webdriver.promise.rejected(new StubError); + arg.thenCatch(goog.nullFunction); // Suppress default handler. + + var driver = testHelper.createDriver(); + return driver.executeScript(goog.nullFunction, arg). + then(fail, assertIsStubError); +} + + +function testExecuteAsyncScript_failsIfArgumentIsARejectedPromise() { + var testHelper = new TestHelper().replayAll(); + + var arg = webdriver.promise.rejected(new StubError); + arg.thenCatch(goog.nullFunction); // Suppress default handler. + + var driver = testHelper.createDriver(); + return driver.executeAsyncScript(goog.nullFunction, arg). + then(fail, assertIsStubError); +} + + +function testFindElement_elementNotFound() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnError(ECode.NO_SUCH_ELEMENT, { + 'message':'Unable to find element' + }). + replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(By.id('foo')); + element.click(); // This should never execute. + return waitForAbort().then( + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); +} + + +function testFindElement_elementNotFoundInACallback() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnError( + ECode.NO_SUCH_ELEMENT, {'message':'Unable to find element'}). + replayAll(); + + var driver = testHelper.createDriver(); + webdriver.promise.fulfilled().then(function() { + var element = driver.findElement(By.id('foo')); + return element.click(); // Should not execute. + }); + return waitForAbort().then( + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); +} + + +function testFindElement_elementFound() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnSuccess({'ELEMENT':'bar'}). + expect(CName.CLICK_ELEMENT, {'id':{'ELEMENT':'bar'}}). + andReturnSuccess(). + replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(By.id('foo')); + element.click(); + return waitForIdle(); +} + + +function testFindElement_canUseElementInCallback() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnSuccess({'ELEMENT':'bar'}). + expect(CName.CLICK_ELEMENT, {'id':{'ELEMENT':'bar'}}). + andReturnSuccess(). + replayAll(); + + var driver = testHelper.createDriver(); + driver.findElement(By.id('foo')).then(function(element) { + element.click(); + }); + return waitForIdle(); +} + + +function testFindElement_byJs() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.body', + 'args': [] + }). + andReturnSuccess({'ELEMENT':'bar'}). + expect(CName.CLICK_ELEMENT, {'id':{'ELEMENT':'bar'}}). + replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(By.js('return document.body')); + element.click(); // just to make sure + return waitForIdle(); +} + + +function testFindElement_byJs_returnsNonWebElementValue() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, {'script': 'return 123', 'args': []}). + andReturnSuccess(123). + replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(By.js('return 123')); + element.click(); // Should not execute. + return waitForAbort().then(function(e) { + assertEquals( + 'Not the expected error message', + 'Custom locator did not return a WebElement', e.message); + }); +} + + +function testFindElement_byJs_canPassArguments() { + var script = 'return document.getElementsByTagName(arguments[0]);'; + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, { + 'script': script, + 'args': ['div'] + }). + andReturnSuccess({'ELEMENT':'one'}). + replayAll(); + var driver = testHelper.createDriver(); + driver.findElement(By.js(script, 'div')); + return waitForIdle(); +} + + +function testFindElement_customLocator() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'tag name', 'value':'a'}). + andReturnSuccess([{'ELEMENT':'foo'}, {'ELEMENT':'bar'}]). + expect(CName.CLICK_ELEMENT, {'id':{'ELEMENT':'foo'}}). + andReturnSuccess(). + replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(function(d) { + assertEquals(driver, d); + return d.findElements(By.tagName('a')); + }); + element.click(); + return waitForIdle(); +} + + +function testFindElement_customLocatorThrowsIfResultIsNotAWebElement() { + var testHelper = new TestHelper().replayAll(); + + var driver = testHelper.createDriver(); + driver.findElement(function() { + return 1; + }); + return waitForAbort().then(function(e) { + assertEquals( + 'Not the expected error message', + 'Custom locator did not return a WebElement', e.message); + }); +} + + +function testIsElementPresent_elementNotFound() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). + andReturnSuccess([]). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.isElementPresent(By.id('foo')).then(assertFalse); +} + + +function testIsElementPresent_elementFound() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). + andReturnSuccess([{'ELEMENT':'bar'}]). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.isElementPresent(By.id('foo')).then(assertTrue); +} + + +function testIsElementPresent_letsErrorsPropagate() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). + andReturnError(ECode.UNKNOWN_ERROR, {'message':'There is no spoon'}). + replayAll(); + + var driver = testHelper.createDriver(); + driver.isElementPresent(By.id('foo')); + return waitForAbort().then( + expectedError(ECode.UNKNOWN_ERROR, 'There is no spoon')); +} + + +function testIsElementPresent_byJs() { + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, {'script': 'return 123', 'args': []}). + andReturnSuccess([{'ELEMENT':'bar'}]). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.isElementPresent(By.js('return 123')).then(assertTrue); +} + + +function testIsElementPresent_byJs_canPassScriptArguments() { + var script = 'return document.getElementsByTagName(arguments[0]);'; + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, { + 'script': script, + 'args': ['div'] + }). + andReturnSuccess({'ELEMENT':'one'}). + replayAll(); + + var driver = testHelper.createDriver(); + driver.isElementPresent(By.js(script, 'div')); + return waitForIdle(); +} + + +function testFindElements() { + var json = [ + {'ELEMENT':'foo'}, + {'ELEMENT':'bar'}, + {'ELEMENT':'baz'} + ]; + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'tag name', 'value':'a'}). + andReturnSuccess(json). + replayAll(); + + var driver = testHelper.createDriver(); + driver.findElements(By.tagName('a')).then(function(elements) { + assertEquals(3, elements.length); + + var callbacks = new Array(3); + assertTypeAndId(0); + assertTypeAndId(1); + assertTypeAndId(2); + + return webdriver.promise.all(callbacks); + + function assertTypeAndId(index) { + assertTrue('Not a WebElement at index ' + index, + elements[index] instanceof webdriver.WebElement); + callbacks[index] = elements[index].getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json[index], id); + }); + } + }); +} + + +function testFindElements_byJs() { + var json = [ + {'ELEMENT':'foo'}, + {'ELEMENT':'bar'}, + {'ELEMENT':'baz'} + ]; + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.getElementsByTagName("div");', + 'args': [] + }). + andReturnSuccess(json). + replayAll(); + + var driver = testHelper.createDriver(); + + return driver. + findElements(By.js('return document.getElementsByTagName("div");')). + then(function(elements) { + var callbacks = new Array(3); + assertEquals(3, elements.length); + + assertTypeAndId(0); + assertTypeAndId(1); + assertTypeAndId(2); + return webdriver.promise.all(callbacks); + + function assertTypeAndId(index) { + assertTrue('Not a WebElement at index ' + index, + elements[index] instanceof webdriver.WebElement); + callbacks[index] = elements[index].getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json[index], id); + }); + } + }); +} + + +function testFindElements_byJs_filtersOutNonWebElementResponses() { + var json = [ + {'ELEMENT':'foo'}, + 123, + 'a', + false, + {'ELEMENT':'bar'}, + {'not a web element': 1}, + {'ELEMENT':'baz'} + ]; + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.getElementsByTagName("div");', + 'args': [] + }). + andReturnSuccess(json). + replayAll(); + + var driver = testHelper.createDriver(); + driver.findElements(By.js('return document.getElementsByTagName("div");')). + then(function(elements) { + assertEquals(3, elements.length); + var callbacks = new Array(3); + assertTypeAndId(0, 0); + assertTypeAndId(1, 4); + assertTypeAndId(2, 6); + return webdriver.promise.all(callbacks); + + function assertTypeAndId(index, jsonIndex) { + assertTrue('Not a WebElement at index ' + index, + elements[index] instanceof webdriver.WebElement); + callbacks[index] = elements[index].getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json[jsonIndex], id); + }); + } + }); + return waitForIdle(); +} + + +function testFindElements_byJs_convertsSingleWebElementResponseToArray() { + var json = {'ELEMENT':'foo'}; + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, { + 'script': 'return document.getElementsByTagName("div");', + 'args': [] + }). + andReturnSuccess(json). + replayAll(); + + var driver = testHelper.createDriver(); + return driver. + findElements(By.js('return document.getElementsByTagName("div");')). + then(function(elements) { + assertEquals(1, elements.length); + assertTrue(elements[0] instanceof webdriver.WebElement); + return elements[0].getId().then(function(id) { + webdriver.test.testutil.assertObjectEquals(json, id); + }); + }); +} + + +function testFindElements_byJs_canPassScriptArguments() { + var script = 'return document.getElementsByTagName(arguments[0]);'; + var testHelper = new TestHelper(). + expect(CName.EXECUTE_SCRIPT, { + 'script': script, + 'args': ['div'] + }). + andReturnSuccess([{'ELEMENT':'one'}, {'ELEMENT':'two'}]). + replayAll(); + + var driver = testHelper.createDriver(); + driver.findElements(By.js(script, 'div')); + return waitForIdle(); +} + + +function testSendKeysConvertsVarArgsIntoStrings_simpleArgs() { + var testHelper = new TestHelper(). + expect(CName.SEND_KEYS_TO_ELEMENT, {'id':{'ELEMENT':'one'}, + 'value':['1','2','abc','3']}). + andReturnSuccess(). + replayAll(); + + var driver = testHelper.createDriver(); + var element = new webdriver.WebElement(driver, {'ELEMENT': 'one'}); + element.sendKeys(1, 2, 'abc', 3); + return waitForIdle(); +} + + +function testSendKeysConvertsVarArgsIntoStrings_promisedArgs() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnSuccess({'ELEMENT':'one'}). + expect(CName.SEND_KEYS_TO_ELEMENT, {'id':{'ELEMENT':'one'}, + 'value':['abc', '123', 'def']}). + andReturnSuccess(). + replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(By.id('foo')); + element.sendKeys( + webdriver.promise.fulfilled('abc'), 123, + webdriver.promise.fulfilled('def')); + return waitForIdle(); +} + + +function testSendKeysWithAFileDetector() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnSuccess({'ELEMENT':'one'}). + expect(CName.SEND_KEYS_TO_ELEMENT, {'id':{'ELEMENT':'one'}, + 'value':['modified/path']}). + andReturnSuccess(). + replayAll(); + + var driver = testHelper.createDriver(); + + var mockDetector = mockControl.createStrictMock(webdriver.FileDetector); + mockDetector.handleFile(driver, 'original/path'). + $returns(webdriver.promise.fulfilled('modified/path')); + mockDetector.$replay(); + + driver.setFileDetector(mockDetector); + + var element = driver.findElement(By.id('foo')); + element.sendKeys('original/', 'path'); +} + +function testElementEquality_isReflexive() { + var a = new webdriver.WebElement(STUB_DRIVER, 'foo'); + return webdriver.WebElement.equals(a, a).then(assertTrue); +} + +function testElementEquals_doesNotSendRpcIfElementsHaveSameId() { + var a = new webdriver.WebElement(STUB_DRIVER, 'foo'), + b = new webdriver.WebElement(STUB_DRIVER, 'foo'), + c = new webdriver.WebElement(STUB_DRIVER, 'foo'); + return webdriver.promise.all([ + webdriver.WebElement.equals(a, b).then( + goog.partial(assertTrue, 'a should == b!')), + webdriver.WebElement.equals(b, a).then( + goog.partial(assertTrue, 'symmetry check failed')), + webdriver.WebElement.equals(a, c).then( + goog.partial(assertTrue, 'a should == c!')), + webdriver.WebElement.equals(b, c).then( + goog.partial(assertTrue, 'transitive check failed')) + ]); +} + +function testElementEquals_sendsRpcIfElementsHaveDifferentIds() { + var id1 = {'ELEMENT':'foo'}; + var id2 = {'ELEMENT':'bar'}; + var testHelper = new TestHelper(). + expect(CName.ELEMENT_EQUALS, {'id':id1, 'other':id2}). + andReturnSuccess(true). + replayAll(); + + var driver = testHelper.createDriver(); + var a = new webdriver.WebElement(driver, id1), + b = new webdriver.WebElement(driver, id2); + return webdriver.WebElement.equals(a, b).then(assertTrue); +} + + +function testElementEquals_failsIfAnInputElementCouldNotBeFound() { + var testHelper = new TestHelper().replayAll(); + + var id = webdriver.promise.rejected(new StubError); + id.thenCatch(goog.nullFunction); // Suppress default handler. + + var driver = testHelper.createDriver(); + var a = new webdriver.WebElement(driver, {'ELEMENT': 'foo'}); + var b = new webdriver.WebElementPromise(driver, id); + + return webdriver.WebElement.equals(a, b).then(fail, assertIsStubError); +} + + +function testWaiting_waitSucceeds() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). + andReturnSuccess([]). + times(2). + expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). + andReturnSuccess([{'ELEMENT':'bar'}]). + replayAll(); + + var driver = testHelper.createDriver(); + driver.wait(function() { + return driver.isElementPresent(By.id('foo')); + }, 200); + return waitForIdle(); +} + + +function testWaiting_waitTimesout_timeoutCaught() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). + andReturnSuccess([]). + anyTimes(). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.wait(function() { + return driver.isElementPresent(By.id('foo')); + }, 25).then(fail, function(e) { + assertEquals('Wait timed out after ', + e.message.substring(0, 'Wait timed out after '.length)); + }); +} + + +function testWaiting_waitTimesout_timeoutNotCaught() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENTS, {'using':'id', 'value':'foo'}). + andReturnSuccess([]). + anyTimes(). + replayAll(); + + var driver = testHelper.createDriver(); + driver.wait(function() { + return driver.isElementPresent(By.id('foo')); + }, 25); + return waitForAbort().then(function(e) { + assertEquals('Wait timed out after ', + e.message.substring(0, 'Wait timed out after '.length)); + }); +} + +function testInterceptsAndTransformsUnhandledAlertErrors() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnError(ECode.UNEXPECTED_ALERT_OPEN, { + 'message': 'boom', + 'alert': {'text': 'hello'} + }). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.findElement(By.id('foo')).then(fail, function(e) { + assertTrue(e instanceof webdriver.UnhandledAlertError); + assertEquals('hello', e.getAlertText()); + }); +} + +function +testUnhandledAlertErrors_usesEmptyStringIfAlertTextOmittedFromResponse() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnError(ECode.UNEXPECTED_ALERT_OPEN, {'message': 'boom'}). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.findElement(By.id('foo')).then(fail, function(e) { + assertTrue(e instanceof webdriver.UnhandledAlertError); + assertEquals('', e.getAlertText()); + }); +} + +function testAlertHandleResolvesWhenPromisedTextResolves() { + var promise = new webdriver.promise.Deferred(); + + var alert = new webdriver.AlertPromise(STUB_DRIVER, promise); + assertTrue(alert.isPending()); + + promise.fulfill(new webdriver.Alert(STUB_DRIVER, 'foo')); + return alert.getText().then(function(text) { + assertEquals('foo', text); + }); +} + + +function testWebElementsBelongToSameFlowAsParentDriver() { + var testHelper = new TestHelper() + .expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}) + .andReturnSuccess({'ELEMENT': 'abc123'}) + .replayAll(); + + var driver = testHelper.createDriver(); + var otherFlow = new webdriver.promise.ControlFlow(); + otherFlow.execute(function() { + driver.findElement({id: 'foo'}).then(function() { + assertEquals( + 'WebElement should belong to the same flow as its parent driver', + driver.controlFlow(), webdriver.promise.controlFlow()); + }); + }); + + assertNotEquals(otherFlow, driver.controlFlow); + return goog.Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); +} + + +function testSwitchToAlertThatIsNotPresent() { + var testHelper = new TestHelper() + .expect(CName.GET_ALERT_TEXT) + .andReturnError(ECode.NO_SUCH_ALERT, {'message': 'no alert'}) + .replayAll(); + + var driver = testHelper.createDriver(); + var alert = driver.switchTo().alert(); + alert.dismiss(); // Should never execute. + return waitForAbort().then(expectedError(ECode.NO_SUCH_ALERT, 'no alert')); +} + + +function testAlertsBelongToSameFlowAsParentDriver() { + var testHelper = new TestHelper() + .expect(CName.GET_ALERT_TEXT).andReturnSuccess('hello') + .replayAll(); + + var driver = testHelper.createDriver(); + var otherFlow = new webdriver.promise.ControlFlow(); + otherFlow.execute(function() { + driver.switchTo().alert().then(function() { + assertEquals( + 'Alert should belong to the same flow as its parent driver', + driver.controlFlow(), webdriver.promise.controlFlow()); + }); + }); + + assertNotEquals(otherFlow, driver.controlFlow); + return goog.Promise.all([ + waitForIdle(otherFlow), + waitForIdle(driver.controlFlow()) + ]); +} + +function testFetchingLogs() { + var testHelper = new TestHelper(). + expect(CName.GET_LOG, {'type': 'browser'}). + andReturnSuccess([ + new webdriver.logging.Entry( + webdriver.logging.Level.INFO, 'hello', 1234), + {'level': 'DEBUG', 'message': 'abc123', 'timestamp': 5678} + ]). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.manage().logs().get('browser').then(function(entries) { + assertEquals(2, entries.length); + + assertTrue(entries[0] instanceof webdriver.logging.Entry); + assertEquals(webdriver.logging.Level.INFO.value, entries[0].level.value); + assertEquals('hello', entries[0].message); + assertEquals(1234, entries[0].timestamp); + + assertTrue(entries[1] instanceof webdriver.logging.Entry); + assertEquals(webdriver.logging.Level.DEBUG.value, entries[1].level.value); + assertEquals('abc123', entries[1].message); + assertEquals(5678, entries[1].timestamp); + }); +} + + +function testCommandsFailIfInitialSessionCreationFailed() { + var testHelper = new TestHelper().replayAll(); + + var session = webdriver.promise.rejected(new StubError); + + var driver = testHelper.createDriver(session); + var navigateResult = driver.get('some-url').then(fail, assertIsStubError); + var quitResult = driver.quit().then(fail, assertIsStubError); + + return waitForIdle().then(function() { + return webdriver.promise.all(navigateResult, quitResult); + }); +} + + +function testWebElementCommandsFailIfInitialDriverCreationFailed() { + var testHelper = new TestHelper().replayAll(); + + var session = webdriver.promise.rejected(new StubError); + + var driver = testHelper.createDriver(session); + return driver.findElement(By.id('foo')).click(). + then(fail, assertIsStubError); +} + + +function testWebElementCommansFailIfElementCouldNotBeFound() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnError(ECode.NO_SUCH_ELEMENT, + {'message':'Unable to find element'}). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.findElement(By.id('foo')).click(). + then(fail, + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); +} + + +function testCannotFindChildElementsIfParentCouldNotBeFound() { + var testHelper = new TestHelper(). + expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}). + andReturnError(ECode.NO_SUCH_ELEMENT, + {'message':'Unable to find element'}). + replayAll(); + + var driver = testHelper.createDriver(); + return driver.findElement(By.id('foo')) + .findElement(By.id('bar')) + .findElement(By.id('baz')) + .then(fail, + expectedError(ECode.NO_SUCH_ELEMENT, 'Unable to find element')); +} + + +function testActionSequenceFailsIfInitialDriverCreationFailed() { + var testHelper = new TestHelper().replayAll(); + + var session = webdriver.promise.rejected(new StubError); + + // Suppress the default error handler so we can verify it propagates + // to the perform() call below. + session.thenCatch(goog.nullFunction); + + return testHelper.createDriver(session). + actions(). + mouseDown(). + mouseUp(). + perform(). + thenCatch(assertIsStubError); +} + + +function testActionSequence_mouseMove_noElement() { + var testHelper = new TestHelper() + .expect(CName.MOVE_TO, {'xoffset': 0, 'yoffset': 125}) + .andReturnSuccess() + .replayAll(); + + return testHelper.createDriver(). + actions(). + mouseMove({x: 0, y: 125}). + perform(); +} + + +function testActionSequence_mouseMove_element() { + var testHelper = new TestHelper() + .expect(CName.FIND_ELEMENT, {'using':'id', 'value':'foo'}) + .andReturnSuccess({'ELEMENT': 'abc123'}) + .expect( + CName.MOVE_TO, {'element': 'abc123', 'xoffset': 0, 'yoffset': 125}) + .andReturnSuccess() + .replayAll(); + + var driver = testHelper.createDriver(); + var element = driver.findElement(By.id('foo')); + return driver.actions() + .mouseMove(element, {x: 0, y: 125}) + .perform(); +} + + +function testActionSequence_mouseDown() { + var testHelper = new TestHelper() + .expect(CName.MOUSE_DOWN, {'button': webdriver.Button.LEFT}) + .andReturnSuccess() + .replayAll(); + + return testHelper.createDriver(). + actions(). + mouseDown(). + perform(); +} + + +function testActionSequence() { + var testHelper = new TestHelper() + .expect(CName.FIND_ELEMENT, {'using':'id', 'value':'a'}) + .andReturnSuccess({'ELEMENT': 'id1'}) + .expect(CName.FIND_ELEMENT, {'using':'id', 'value':'b'}) + .andReturnSuccess({'ELEMENT': 'id2'}) + .expect(CName.SEND_KEYS_TO_ACTIVE_ELEMENT, + {'value': [webdriver.Key.SHIFT]}) + .andReturnSuccess() + .expect(CName.MOVE_TO, {'element': 'id1'}) + .andReturnSuccess() + .expect(CName.CLICK, {'button': webdriver.Button.LEFT}) + .andReturnSuccess() + .expect(CName.MOVE_TO, {'element': 'id2'}) + .andReturnSuccess() + .expect(CName.CLICK, {'button': webdriver.Button.LEFT}) + .andReturnSuccess() + .replayAll(); + + var driver = testHelper.createDriver(); + var element1 = driver.findElement(By.id('a')); + var element2 = driver.findElement(By.id('b')); + + return driver.actions() + .keyDown(webdriver.Key.SHIFT) + .click(element1) + .click(element2) + .perform(); +} + + +function testAlertCommandsFailIfAlertNotPresent() { + var testHelper = new TestHelper() + .expect(CName.GET_ALERT_TEXT) + .andReturnError(ECode.NO_SUCH_ALERT, {'message': 'no alert'}) + .replayAll(); + + var driver = testHelper.createDriver(); + var alert = driver.switchTo().alert(); + + var expectError = expectedError(ECode.NO_SUCH_ALERT, 'no alert'); + var callbacks = []; + for (var key in webdriver.Alert.prototype) { + if (webdriver.Alert.prototype.hasOwnProperty(key)) { + callbacks.push(key, alert[key].call(alert).thenCatch(expectError)); + } + } + + return waitForIdle().then(function() { + return webdriver.promise.all(callbacks); + }); +} diff --git a/lib/webdriver/testing/asserts.js b/lib/webdriver/testing/asserts.js index bbe979d..b8ba02f 100644 --- a/lib/webdriver/testing/asserts.js +++ b/lib/webdriver/testing/asserts.js @@ -1,16 +1,19 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Assertions and expectation utilities for use in WebDriver test @@ -373,7 +376,7 @@ webdriver.testing.Assertion.prototype.isFalse = function() { * @extends {webdriver.testing.Assertion} */ webdriver.testing.NegatedAssertion = function(value) { - goog.base(this, value); + webdriver.testing.NegatedAssertion.base(this, 'constructor', value); this.value = value; }; goog.inherits( @@ -384,11 +387,10 @@ goog.inherits( webdriver.testing.NegatedAssertion.prototype.apply = function( matcher, opt_message) { matcher = new goog.labs.testing.IsNotMatcher(matcher); - return goog.base(this, 'apply', matcher, opt_message); + return webdriver.testing.NegatedAssertion.base(this, 'apply', matcher, + opt_message); }; - - /** * Creates a new assertion. * @param {*} value The value to perform an assertion on. diff --git a/lib/webdriver/testing/client.js b/lib/webdriver/testing/client.js new file mode 100644 index 0000000..ff13842 --- /dev/null +++ b/lib/webdriver/testing/client.js @@ -0,0 +1,179 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.provide('webdriver.testing.Client'); + +goog.require('goog.json'); +goog.require('goog.net.XmlHttp'); + + + +/** + * The client responsible for publishing test events to the server. Each event + * will be published using a POST {@link goog.net.XmlHttp} request. The body of + * each request will be a JSON object with the following fields: + *
          + *
        • id: An identifier for this client, derived from the window + * locations' pathname. + *
        • type: The type of event. + *
        • data: A JSONObject whose contents will be specific to each event type. + *
        + * + * @param {Window=} opt_win The window to pull the path name from for this + * client. Defaults to the current window. + * @param {string=} opt_url The URL to publish test events to. Defaults to + * {@link webdriver.testing.Client.DEFAULT_URL}. + * @constructor + */ +webdriver.testing.Client = function(opt_win, opt_url) { + + /** @private {string} */ + this.id_ = (opt_win || window).location.pathname; + + /** @private {string} */ + this.url_ = opt_url || webdriver.testing.Client.DEFAULT_URL; +}; + + +/** + * Default URL to publish test events to. + * @type {string} + * @const + */ +webdriver.testing.Client.DEFAULT_URL = '/testevent'; + + +/** + * The types of events that may be published by a TestClient to the server. + * @enum {string} + * @private + */ +webdriver.testing.Client.EventType_ = { + + /** Sent to signal that a test suite has been fully initialized. */ + INIT: 'INIT', + + /** + * Sent when starting a new test. The data object will have the following + * fields: + * - name: The name of the test. + */ + START_TEST: 'START_TEST', + + /** + * Sent when an error has occurred. The data object will have the following + * fields: + * - message: The error message. + */ + ERROR: 'ERROR', + + /** + * Sent when all tests have completed. The data object will have the following + * fields: + * - isSuccess: Whether the tests succeeded. + * - report: A flat log for the test suite. + */ + RESULTS: 'RESULTS', + + /** + * Sent when there is a screenshot for the server to record. The included data + * object will have two fields: + * - name: A debug label for the screenshot. + * - data: The PNG screenshot as a base64 string. + */ + SCREENSHOT: 'SCREENSHOT' +}; + + +/** + * Sends a simple message to the server, notifying it that the test runner has + * been initialized. + */ +webdriver.testing.Client.prototype.sendInitEvent = function() { + this.sendEvent_(webdriver.testing.Client.EventType_.INIT); +}; + + +/** + * Sends an error event. + * @param {string} message The error message. + */ +webdriver.testing.Client.prototype.sendErrorEvent = function(message) { + this.sendEvent_(webdriver.testing.Client.EventType_.ERROR, { + 'message': message + }); +}; + + +/** + * Sends an event indicating that a new test has started. + * @param {string} name The name of the test. + */ +webdriver.testing.Client.prototype.sendTestStartedEvent = function(name) { + this.sendEvent_(webdriver.testing.Client.EventType_.START_TEST, { + 'name': name + }); +}; + + +/** + * Sends an event to the server indicating that tests have completed. + * @param {boolean} isSuccess Whether the tests finished successfully. + * @param {string} report The test log. + */ +webdriver.testing.Client.prototype.sendResultsEvent = function(isSuccess, + report) { + this.sendEvent_(webdriver.testing.Client.EventType_.RESULTS, { + 'isSuccess': isSuccess, + 'report': report + }); +}; + + +/** +* Sends a screenshot to be recorded by the server. +* @param {string} data The PNG screenshot as a base64 string. +* @param {string=} opt_name A debug label for the screenshot; if omitted, the +* server will generate a random name. +*/ +webdriver.testing.Client.prototype.sendScreenshotEvent = function(data, + opt_name) { + this.sendEvent_(webdriver.testing.Client.EventType_.SCREENSHOT, { + 'name': opt_name, + 'data': data + }); +}; + + +/** +* Sends an event to the server. +* @param {string} type The type of event to send. +* @param {!Object=} opt_data JSON data object to send with the event, if any. +* @private +*/ +webdriver.testing.Client.prototype.sendEvent_ = function(type, opt_data) { + var payload = goog.json.serialize({ + 'id': this.id_, + 'type': type, + 'data': opt_data || {} + }); + + var xhr = new goog.net.XmlHttp; + xhr.open('POST', this.url_, true); + xhr.send(payload); + // TODO: Log if the event was not sent properly. +}; diff --git a/lib/webdriver/testing/flowtester.js b/lib/webdriver/testing/flowtester.js new file mode 100644 index 0000000..db9ffff --- /dev/null +++ b/lib/webdriver/testing/flowtester.js @@ -0,0 +1,372 @@ +// Copyright 2013 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. + +goog.provide('webdriver.testing.Clock'); +goog.provide('webdriver.testing.promise.FlowTester'); + +goog.require('goog.array'); +goog.require('webdriver.promise.ControlFlow'); + + + +/** + * Describes an object that can be used to advance the clock and trigger + * timeouts registered on the global timing functions. + * @interface + */ +webdriver.testing.Clock = function() {}; + + +/** + * Advances the clock. + * @param {number=} opt_ms The number of milliseconds to advance the clock by. + * Defaults to 1 ms. + */ +webdriver.testing.Clock.prototype.tick = function(opt_ms) {}; + + + +/** + * Utility for writing unit tests against a + * {@link webdriver.promise.ControlFlow}. This class assumes the global + * timeout functions (e.g. setTimeout) have been replaced with test doubles. + * These doubles should allow registered timeouts to be triggered by + * calling {@code clock.tick()}. + * @param {!webdriver.testing.Clock} clock The fake clock to use. + * @param {{clearInterval: function(number), + * clearTimeout: function(number), + * setInterval: function(!Function, number): number, + * setTimeout: function(!Function, number): number}} timer + * The timer object to use for the application under test. + * @constructor + * @implements {goog.disposable.IDisposable} + */ +webdriver.testing.promise.FlowTester = function(clock, timer) { + + var self = this; + + /** @private {!webdriver.testing.Clock} */ + this.clock_ = clock; + + /** + * @private {!Array.<{isIdle: boolean, + * errors: !Array., + * flow: !webdriver.promise.ControlFlow}>} + */ + this.allFlows_ = []; + + /** @private {!webdriver.promise.ControlFlow} */ + this.flow_ = createFlow(); + + /** @private {!webdriver.promise.ControlFlow} */ + this.originalFlow_ = webdriver.promise.controlFlow(); + webdriver.promise.setDefaultFlow(this.flow_); + + /** + * @private {function(function(!webdriver.promise.ControlFlow)): + * !webdriver.promise.Promise} + */ + this.originalCreateFlow_ = webdriver.promise.createFlow; + + /** + * @param {function(!webdriver.promise.ControlFlow)} callback The entry point + * to the newly created flow. + * @return {!webdriver.promise.Promise} . + */ + webdriver.promise.createFlow = function(callback) { + var flow = createFlow(); + return flow.execute(function() { + return callback(flow); + }); + }; + + function createFlow() { + var record = { + isIdle: true, + errors: [], + flow: new webdriver.promise.ControlFlow(timer). + on(webdriver.promise.ControlFlow.EventType.IDLE, function() { + record.isIdle = true; + }). + on(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK, function() { + record.isIdle = false; + }). + on(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, + function(e) { + record.isIdle = true; + record.errors.push(e); + }) + }; + self.allFlows_.push(record); + return record.flow; + } +}; + + +/** @private {boolean} */ +webdriver.testing.promise.FlowTester.prototype.isDisposed_ = false; + + +/** @override */ +webdriver.testing.promise.FlowTester.prototype.isDisposed = function() { + return this.isDisposed_; +}; + + +/** + * Disposes of this instance, restoring the default control flow object. + * @override + */ +webdriver.testing.promise.FlowTester.prototype.dispose = function() { + if (!this.isDisposed_) { + goog.array.forEach(this.allFlows_, function(record) { + record.flow.reset(); + }); + webdriver.promise.setDefaultFlow(this.originalFlow_); + webdriver.promise.createFlow = this.originalCreateFlow_; + this.isDisposed_ = true; + } +}; + + +/** + * @param {!Array} messages The array to join into a single error message. + * @throws {Error} An error with a message formatted from {@code messages}. + * @private + */ +webdriver.testing.promise.FlowTester.prototype.throwWithMessages_ = function( + messages) { + throw Error(messages.join('\n--------\n') + '\n === done ==='); +}; + + +/** + * Verifies that all created flows are idle and completed without any errors. + * If a specific flow object is provided, will only check that flow. + * @param {webdriver.promise.ControlFlow=} opt_flow The specific flow to check. + * If not specified, will verify against all flows. + */ +webdriver.testing.promise.FlowTester.prototype.verifySuccess = function( + opt_flow) { + var messages = []; + var foundFlow = false; + + goog.array.forEach(this.allFlows_, function(record, index) { + if (!opt_flow || opt_flow === record.flow) { + foundFlow = true; + if (record.errors.length) { + messages = goog.array.concat( + messages, 'Uncaught errors for flow #' + index, + goog.array.map(record.errors, function(error) { + if (!error) { + error = error + ''; + } + return error.stack || error.message || String(error); + })); + } + + if (!record.isIdle) { + messages.push('Flow #' + index + ' is not idle'); + } + } + }); + + if (opt_flow && !foundFlow) { + messages.push('Specified flow not found!'); + } + + if (messages.length) { + this.throwWithMessages_(messages); + } +}; + + +/** + * Verifies that all flows are idle and at least one reported a single failure. + * If a specific flow object is provided, will only check that flow. + * @param {webdriver.promise.ControlFlow=} opt_flow The flow expected to have + * generated the error. + */ +webdriver.testing.promise.FlowTester.prototype.verifyFailure = function( + opt_flow) { + var messages = []; + var foundFlow = false; + var foundAFailure = false; + + goog.array.forEach(this.allFlows_, function(record, index) { + if (!opt_flow || opt_flow === record.flow) { + foundFlow = true; + + if (!record.isIdle) { + messages.push('Flow #' + index + ' is not idle'); + } + + if (record.errors.length) { + foundAFailure = true; + + if (record.errors.length > 1) { + messages = goog.array.concat( + messages, 'Flow #' + index + ' had multiple errors: ', + record.errors); + } + } + + } + }); + + if (opt_flow && !foundFlow) { + messages.push('Specified flow not found!'); + } + + if (!foundAFailure) { + messages.push('No failures found!'); + } + + if (messages.length) { + this.throwWithMessages_(messages); + } +}; + + +/** + * @param {webdriver.promise.ControlFlow=} opt_flow The specific control flow + * to check. If not specified, will implicitly verify there was a single + * error across all flows. + * @return {Error} The error reported by the application. + * @throws {Error} If the application is not finished, or did not report + * exactly one error. + */ +webdriver.testing.promise.FlowTester.prototype.getFailure = function( + opt_flow) { + this.verifyFailure(opt_flow); + + var errors = []; + goog.array.forEach(this.allFlows_, function(record) { + if (!opt_flow || opt_flow === record.flow) { + errors = goog.array.concat(errors, record.errors); + } + }); + + if (errors.length > 1) { + this.throwWithMessages_(goog.array.concat( + 'There was more than one failure', errors)); + } + + return errors[0]; +}; + + +/** + * Advances the clock so the {@link webdriver.promise.ControlFlow}'s event + * loop will run once. + */ +webdriver.testing.promise.FlowTester.prototype.turnEventLoop = function() { + this.clock_.tick(webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY); +}; + + +/** + * @param {webdriver.promise.ControlFlow=} opt_flow The specific flow to check. + * If not specified, will verify all flows are still running. + * @throws {Error} If the application is not running. + */ +webdriver.testing.promise.FlowTester.prototype.assertStillRunning = function( + opt_flow) { + var messages = []; + var foundFlow = false; + + goog.array.forEach(this.allFlows_, function(record, index) { + if (!opt_flow || opt_flow === record.flow) { + foundFlow = true; + if (record.isIdle) { + messages.push('Flow #' + index + ' is idle'); + } + + if (record.errors.length) { + messages = goog.array.concat( + messages, 'Uncaught errors for flow #' + index, record.errors); + } + } + }); + + if (opt_flow && !foundFlow) { + messages.push('Specified flow not found!'); + } + + if (messages.length) { + this.throwWithMessages_(messages); + } +}; + + +/** + * Runs the application, turning its event loop until it is expected to have + * shutdown (as indicated by having no more frames, or no frames with pending + * tasks). + */ +webdriver.testing.promise.FlowTester.prototype.run = function() { + var flow = this.flow_; + var self = this; + var done = false; + var shouldBeDone = false; + + while (!done) { + this.turnEventLoop(); + if (shouldBeDone) { + assertIsDone(); + } else { + determineIfShouldBeDone(); + } + + // If the event loop generated an unhandled promise, it won't be reported + // until one more turn of the JS event loop, so we need to tick the + // clock once more. This is necessary for our tests to simulate a real + // JS environment. + this.clock_.tick(); + } + + function assertIsDone() { + // Shutdown is done in one extra turn of the event loop. + self.clock_.tick(); + done = goog.array.every(self.allFlows_, function(record) { + return record.isIdle; + }); + + if (!done) { + goog.array.forEach(self.allFlows_, function(record) { + if (record.flow.activeFrame_) { + // Not done yet, but there are no frames left. This can happen if the + // very first scheduled task was scheduled inside of a promise + // callback. Turn the event loop one more time; the flow should detect + // that it is now finished and start the shutdown procedure. Don't + // recurse here since we could go into an infinite loop if the flow is + // broken. + self.turnEventLoop(); + self.clock_.tick(); + } + }); + } + + if (!done) { + throw Error('Should be done now: ' + flow.getSchedule()); + } + } + + function determineIfShouldBeDone() { + shouldBeDone = flow === webdriver.promise.controlFlow() && + goog.array.every(self.allFlows_, function(record) { + return !record.flow.activeFrame_; + }); + } +}; diff --git a/lib/webdriver/testing/jsunit.js b/lib/webdriver/testing/jsunit.js new file mode 100644 index 0000000..bfb1365 --- /dev/null +++ b/lib/webdriver/testing/jsunit.js @@ -0,0 +1,363 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * @fileoverview File to include for turning any HTML file page into a WebDriver + * JSUnit test suite by configuring an onload listener to the body that will + * instantiate and start the test runner. + */ + +goog.provide('webdriver.testing.jsunit'); +goog.provide('webdriver.testing.jsunit.TestRunner'); + +goog.require('goog.testing.TestRunner'); +goog.require('webdriver.testing.Client'); +goog.require('webdriver.testing.TestCase'); + + + +/** + * Constructs a test runner. + * @param {!webdriver.testing.Client} client . + * @constructor + * @extends {goog.testing.TestRunner} + */ +webdriver.testing.jsunit.TestRunner = function(client) { + goog.base(this); + + /** @private {!webdriver.testing.Client} */ + this.client_ = client; +}; +goog.inherits(webdriver.testing.jsunit.TestRunner, goog.testing.TestRunner); + + +/** + * Element created in the document to add test results to. + * @private {Element} + */ +webdriver.testing.jsunit.TestRunner.prototype.logEl_ = null; + + +/** + * DOM element used to stored screenshots. Screenshots are stored in the DOM to + * avoid exhausting JS stack-space. + * @private {Element} + */ +webdriver.testing.jsunit.TestRunner.prototype.screenshotCacheEl_ = null; + + +/** @override */ +webdriver.testing.jsunit.TestRunner.prototype.initialize = function(testCase) { + goog.base(this, 'initialize', testCase); + this.screenshotCacheEl_ = document.createElement('div'); + document.body.appendChild(this.screenshotCacheEl_); + this.screenshotCacheEl_.style.display = 'none'; +}; + + +/** @override */ +webdriver.testing.jsunit.TestRunner.prototype.execute = function() { + if (!this.testCase) { + throw Error('The test runner must be initialized with a test case before ' + + 'execute can be called.'); + } + this.screenshotCacheEl_.innerHTML = ''; + this.client_.sendInitEvent(); + this.testCase.setCompletedCallback(goog.bind(this.onComplete_, this)); + this.testCase.runTests(); +}; + + +/** + * Writes a nicely formatted log out to the document. Overrides + * {@link goog.testing.TestRunner#writeLog} to handle writing screenshots to the + * log. + * @param {string} log The string to write. + * @override + */ +webdriver.testing.jsunit.TestRunner.prototype.writeLog = function(log) { + var lines = log.split('\n'); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var color; + var isFailOrError = /FAILED/.test(line) || /ERROR/.test(line); + var isScreenshot = / \[SCREENSHOT\] /.test(line); + if (/PASSED/.test(line)) { + color = 'darkgreen'; + } else if (isFailOrError) { + color = 'darkred'; + } else if (isScreenshot) { + color = 'darkblue'; + } else { + color = '#333'; + } + + var div = document.createElement('div'); + if (line.substr(0, 2) == '> ') { + // The stack trace may contain links so it has to be interpreted as HTML. + div.innerHTML = line; + } else { + div.appendChild(document.createTextNode(line)); + } + + if (isFailOrError) { + var testNameMatch = /(\S+) (\[[^\]]*] )?: (FAILED|ERROR)/.exec(line); + if (testNameMatch) { + // Build a URL to run the test individually. If this test was already + // part of another subset test, we need to overwrite the old runTests + // query parameter. We also need to do this without bringing in any + // extra dependencies, otherwise we could mask missing dependency bugs. + var newSearch = 'runTests=' + testNameMatch[1]; + var search = window.location.search; + if (search) { + var oldTests = /runTests=([^&]*)/.exec(search); + if (oldTests) { + newSearch = search.substr(0, oldTests.index) + + newSearch + + search.substr(oldTests.index + oldTests[0].length); + } else { + newSearch = search + '&' + newSearch; + } + } else { + newSearch = '?' + newSearch; + } + var href = window.location.href; + var hash = window.location.hash; + if (hash && hash.charAt(0) != '#') { + hash = '#' + hash; + } + href = href.split('#')[0].split('?')[0] + newSearch + hash; + + // Add the link. + var a = document.createElement('A'); + a.innerHTML = '(run individually)'; + a.style.fontSize = '0.8em'; + a.href = href; + div.appendChild(document.createTextNode(' ')); + div.appendChild(a); + } + } + + if (isScreenshot && this.screenshotCacheEl_.childNodes.length) { + var nextScreenshot = this.screenshotCacheEl_.childNodes[0]; + this.screenshotCacheEl_.removeChild(nextScreenshot); + + a = document.createElement('A'); + a.style.fontSize = '0.8em'; + a.href = 'javascript:void(0);'; + a.onclick = goog.partial(toggleVisibility, a, nextScreenshot); + toggleVisibility(a, nextScreenshot); + div.appendChild(document.createTextNode(' ')); + div.appendChild(a); + } + + div.style.color = color; + div.style.font = 'normal 100% monospace'; + + try { + div.style.whiteSpace = 'pre-wrap'; + } catch (e) { + // NOTE(user): IE raises an exception when assigning to pre-wrap. + // Thankfully, it doesn't collapse whitespace when using monospace fonts, + // so it will display correctly if we ignore the exception. + } + + if (i < 2) { + div.style.fontWeight = 'bold'; + } + this.logEl_.appendChild(div); + + if (nextScreenshot) { + a = document.createElement('A'); + // Accessing the |src| property in IE sometimes results in an + // "Invalid pointer" error, which indicates it has been garbage + // collected. This does not occur when using getAttribute. + a.href = nextScreenshot.getAttribute('src'); + a.target = '_blank'; + a.appendChild(nextScreenshot); + this.logEl_.appendChild(a); + nextScreenshot = null; + } + } + + function toggleVisibility(link, img) { + if (img.style.display === 'none') { + img.style.display = ''; + link.innerHTML = '(hide screenshot)'; + } else { + img.style.display = 'none'; + link.innerHTML = '(view screenshot)'; + } + } +}; + + +/** + * Copied from goog.testing.TestRunner.prototype.onComplete_, which has private + * visibility. + * @private + */ +webdriver.testing.jsunit.TestRunner.prototype.onComplete_ = function() { + var log = this.testCase.getReport(true); + if (this.errors.length > 0) { + log += '\n' + this.errors.join('\n'); + } + + if (!this.logEl_) { + this.logEl_ = document.createElement('div'); + document.body.appendChild(this.logEl_); + } + + // Remove all children from the log element. + var logEl = this.logEl_; + while (logEl.firstChild) { + logEl.removeChild(logEl.firstChild); + } + + this.writeLog(log); + this.client_.sendResultsEvent(this.isSuccess(), this.getReport(true)); +}; + + +/** + * Takes a screenshot. In addition to saving the screenshot for viewing in the + * HTML logs, the screenshot will also be saved using + * @param {!webdriver.WebDriver} driver The driver to take the screenshot with. + * @param {string=} opt_label An optional debug label to identify the screenshot + * with. + * @return {!webdriver.promise.Promise} A promise that will be resolved to the + * screenshot as a base-64 encoded PNG. + */ +webdriver.testing.jsunit.TestRunner.prototype.takeScreenshot = function( + driver, opt_label) { + if (!this.isInitialized()) { + throw Error( + 'The test runner must be initialized before it may be used to' + + ' take screenshots'); + } + + var client = this.client_; + var testCase = this.testCase; + var screenshotCache = this.screenshotCacheEl_; + return driver.takeScreenshot().then(function(png) { + client.sendScreenshotEvent(png, opt_label); + + var img = document.createElement('img'); + img.src = 'data:image/png;base64,' + png; + img.style.border = '1px solid black'; + img.style.maxWidth = '500px'; + screenshotCache.appendChild(img); + + if (testCase) { + testCase.saveMessage('[SCREENSHOT] ' + (opt_label || '')); + } + return png; + }); +}; + + +/** + * Sends a base64 encoded PNG image to the server to be saved in the test + * outputs. + * @param {string} data The base64 encoded PNG image to be sent to the server. + * @param {string=} opt_label An optional debug label to identify the + * screenshot with. + */ +webdriver.testing.jsunit.TestRunner.prototype.saveImage = function( + data, opt_label) { + if (!this.isInitialized()) { + throw Error( + 'The test runner must be initialized before it may be used to' + + ' save images'); + } + + this.client_.sendScreenshotEvent(data, opt_label); + + var img = document.createElement('img'); + img.src = 'data:image/png;base64,' + data; + img.style.border = '1px solid black'; + img.style.maxWidth = '500px'; + this.screenshotCacheEl_.appendChild(img); + + if (this.testCase) { + this.testCase.saveMessage('[SCREENSHOT] ' + (opt_label || '')); + } +}; + + +(function() { + var client = new webdriver.testing.Client(); + var tr = new webdriver.testing.jsunit.TestRunner(client); + + // Export our test runner so it can be accessed by Selenium/WebDriver. This + // will only work if webdriver.WebDriver is using a pure-JavaScript + // webdriver.CommandExecutor. Otherwise, the JS-client could change the + // driver's focus to another window or frame and the Java/Python-client + // wouldn't be able to access this object. + goog.exportSymbol('G_testRunner', tr); + goog.exportSymbol('G_testRunner.initialize', tr.initialize); + goog.exportSymbol('G_testRunner.isInitialized', tr.isInitialized); + goog.exportSymbol('G_testRunner.isFinished', tr.isFinished); + goog.exportSymbol('G_testRunner.isSuccess', tr.isSuccess); + goog.exportSymbol('G_testRunner.getReport', tr.getReport); + goog.exportSymbol('G_testRunner.getRunTime', tr.getRunTime); + goog.exportSymbol('G_testRunner.getNumFilesLoaded', tr.getNumFilesLoaded); + goog.exportSymbol('G_testRunner.setStrict', tr.setStrict); + goog.exportSymbol('G_testRunner.logTestFailure', tr.logTestFailure); + + // Export debug as a global function for JSUnit compatibility. This just + // calls log on the current test case. + if (!goog.global['debug']) { + goog.exportSymbol('debug', goog.bind(tr.log, tr)); + } + + // Add an error handler to report errors that may occur during + // initialization of the page. + var onerror = window.onerror; + window.onerror = function(error, url, line) { + // Call any existing onerror handlers. + if (onerror) { + onerror(error, url, line); + } + if (typeof error == 'object') { + // Webkit started passing an event object as the only argument to + // window.onerror. It doesn't contain an error message, url or line + // number. We therefore log as much info as we can. + if (error.target && error.target.tagName == 'SCRIPT') { + tr.logError('UNKNOWN ERROR: Script ' + error.target.src); + } else { + tr.logError('UNKNOWN ERROR: No error information available.'); + } + } else { + tr.logError('JS ERROR: ' + error + '\nURL: ' + url + '\nLine: ' + line); + } + }; + + var onload = window.onload; + window.onload = function() { + // Call any existing onload handlers. + if (onload) { + onload(); + } + + var testCase = new webdriver.testing.TestCase(client, document.title); + testCase.autoDiscoverTests(); + + tr.initialize(testCase); + tr.execute(); + }; +})(); diff --git a/lib/webdriver/testing/testcase.js b/lib/webdriver/testing/testcase.js new file mode 100644 index 0000000..8c2bc32 --- /dev/null +++ b/lib/webdriver/testing/testcase.js @@ -0,0 +1,168 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Defines a special test case that runs each test inside of a + * {@code webdriver.Application}. This allows each phase to schedule + * asynchronous actions that run to completion before the next phase of the + * test. + * + * This file requires the global {@code G_testRunner} to be initialized before + * use. This can be accomplished by also importing + * {@link webdriver.testing.jsunit}. This namespace is not required by default + * to improve interoperability with other namespaces that may initialize + * G_testRunner. + */ + +goog.provide('webdriver.testing.TestCase'); + +goog.require('goog.testing.TestCase'); +goog.require('webdriver.promise'); +/** @suppress {extraRequire} Imported for user convenience. */ +goog.require('webdriver.testing.asserts'); + + + +/** + * Constructs a test case that synchronizes each test case with the singleton + * {@code webdriver.promise.ControlFlow}. + * + * @param {!webdriver.testing.Client} client The test client to use for + * reporting test results. + * @param {string=} opt_name The name of the test case, defaults to + * 'Untitled Test Case'. + * @constructor + * @extends {goog.testing.TestCase} + */ +webdriver.testing.TestCase = function(client, opt_name) { + goog.base(this, opt_name); + + /** @private {!webdriver.testing.Client} */ + this.client_ = client; +}; +goog.inherits(webdriver.testing.TestCase, goog.testing.TestCase); + + +/** + * Executes the next test inside its own {@code webdriver.Application}. + * @override + */ +webdriver.testing.TestCase.prototype.cycleTests = function() { + var test = this.next(); + if (!test) { + this.finalize(); + return; + } + + goog.testing.TestCase.currentTestName = test.name; + this.result_.runCount++; + this.log('Running test: ' + test.name); + this.client_.sendTestStartedEvent(test.name); + + var self = this; + var hadError = false; + var app = webdriver.promise.controlFlow(); + + this.runSingleTest_(test, onError).then(function() { + hadError || self.doSuccess(test); + self.timeout(function() { + self.cycleTests(); + }, 100); + }); + + function onError(e) { + hadError = true; + self.doError(test, e); + // Note: result_ is a @protected field but still uses the trailing + // underscore. + var err = self.result_.errors[self.result_.errors.length - 1]; + self.client_.sendErrorEvent(err.toString()); + } +}; + + +/** @override */ +webdriver.testing.TestCase.prototype.logError = function(name, opt_e) { + var errMsg = null; + var stack = null; + if (opt_e) { + this.log(opt_e); + if (goog.isString(opt_e)) { + errMsg = opt_e; + } else { + errMsg = opt_e.toString(); + stack = opt_e.stack.substring(errMsg.length + 1); + } + } else { + errMsg = 'An unknown error occurred'; + } + var err = new goog.testing.TestCase.Error(name, errMsg, stack); + + // Avoid double logging. + if (!opt_e || !opt_e['isJsUnitException'] || + !opt_e['loggedJsUnitException']) { + this.saveMessage(err.toString()); + } + + if (opt_e && opt_e['isJsUnitException']) { + opt_e['loggedJsUnitException'] = true; + } + + return err; +}; + + +/** + * Executes a single test, scheduling each phase with the global application. + * Each phase will wait for the application to go idle before moving on to the + * next test phase. This function models the follow basic test flow: + * + * try { + * this.setUp.call(test.scope); + * test.ref.call(test.scope); + * } catch (ex) { + * onError(ex); + * } finally { + * try { + * this.tearDown.call(test.scope); + * } catch (e) { + * onError(e); + * } + * } + * + * @param {!goog.testing.TestCase.Test} test The test to run. + * @param {function(*)} onError The function to call each time an error is + * detected. + * @return {!webdriver.promise.Promise} A promise that will be resolved when the + * test has finished running. + * @private + */ +webdriver.testing.TestCase.prototype.runSingleTest_ = function(test, onError) { + var flow = webdriver.promise.controlFlow(); + + return execute(test.name + '.setUp()', this.setUp)(). + then(execute(test.name + '()', test.ref)). + thenCatch(onError). + then(execute(test.name + '.tearDown()', this.tearDown)). + thenCatch(onError); + + function execute(description, fn) { + return function() { + return flow.execute(goog.bind(fn, test.scope), description); + } + } +}; diff --git a/lib/webdriver/testing/window.js b/lib/webdriver/testing/window.js new file mode 100644 index 0000000..dd56733 --- /dev/null +++ b/lib/webdriver/testing/window.js @@ -0,0 +1,244 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/** + * @fileoverview A utility class for working with test windows. + */ + +goog.provide('webdriver.testing.Window'); + +goog.require('goog.string'); +goog.require('webdriver.promise'); + + + +/** + * Class for managing a window. + * + * This class is implemented as a promise so consumers may register + * callbacks on it to handle situations where the window fails to open. + * + * For example: + * + * var testWindow = webdriver.testing.Window.create(driver); + * // Throw a custom error when the window fails to open. + * testWindow.thenCatch(function(e) { + * throw Error('Failed to open test window: ' + e); + * }); + * + * @param {!webdriver.WebDriver} driver The driver to use. + * @param {(string|!webdriver.promise.Promise)} handle Either the managed + * window's handle, or a promise that will resolve to it. + * @param {(Window|webdriver.promise.Promise)=} opt_window The raw window + * object, if available. + * @constructor + * @extends {webdriver.promise.Promise} + */ +webdriver.testing.Window = function(driver, handle, opt_window) { + webdriver.promise.Promise.call(this, function(fulfill, reject) { + handle.then(fulfill, reject); + }); + + /** @private {!webdriver.WebDriver} */ + this.driver_ = driver; + + /** @private {!webdriver.promise.Promise} */ + this.handle_ = webdriver.promise.when(handle); + + /** @private {!webdriver.promise.Promise} */ + this.window_ = webdriver.promise.when(opt_window); +}; +goog.inherits(webdriver.testing.Window, webdriver.promise.Promise); + + +/** + * Default amount of time, in milliseconds, to wait for a new window to open. + * @type {number} + * @const + */ +webdriver.testing.Window.DEFAULT_OPEN_TIMEOUT = 2000; + + +/** + * Window running this script. Lazily initialized the first time it is requested + * from {@link webdriver.testing.Window.findWindow}. + * @private {webdriver.testing.Window} + */ +webdriver.testing.Window.currentWindow_ = null; + + +/** + * Opens and focuses on a new window. + * + * @param {!webdriver.WebDriver} driver The driver to use. + * @param {?{width: number, height: number}=} opt_size The desired size for + * the new window. + * @param {number=} opt_timeout How long, in milliseconds, to wait for the new + * window to open. Defaults to + * {@link webdriver.testing.Window.DEFAULT_OPEN_TIMEOUT}. + * @return {!webdriver.testing.Window} The new window. + */ +webdriver.testing.Window.create = function(driver, opt_size, opt_timeout) { + var windowPromise = webdriver.promise.defer(); + var handle = driver.call(function() { + var features = [ + 'location=yes', + 'titlebar=yes' + ]; + + if (opt_size) { + features.push( + 'width=' + opt_size.width, + 'height=' + opt_size.height); + } + + var name = goog.string.getRandomString(); + windowPromise.fulfill(window.open('', name, features.join(','))); + + driver.wait(function() { + return driver.switchTo().window(name).then( + function() { return true; }, + function() { return false; }); + }, opt_timeout || webdriver.testing.Window.DEFAULT_OPEN_TIMEOUT); + return driver.getWindowHandle(); + }); + + return new webdriver.testing.Window(driver, handle, windowPromise); +}; + + +/** + * Changes focus to the topmost window for the provided DOM window. + * + * @param {!webdriver.WebDriver} driver The driver to use. + * @param {Window=} opt_window The window to search for. Defaults to the window + * running this script. + * @return {!webdriver.testing.Window} The located window. + */ +webdriver.testing.Window.focusOnWindow = function(driver, opt_window) { + var name = goog.string.getRandomString(); + var win = opt_window ? opt_window.top : window; + + var ret; + if (win === window && webdriver.testing.Window.currentWindow_) { + ret = webdriver.testing.Window.currentWindow_; + webdriver.testing.Window.currentWindow_.focus(); + } else { + win.name = name; + ret = new webdriver.testing.Window(driver, + driver.switchTo().window(name). + then(goog.bind(driver.getWindowHandle, driver)), + win); + if (win === window) { + webdriver.testing.Window.currentWindow_ = ret; + } + } + return ret; +}; + + +/** @override */ +webdriver.testing.Window.prototype.cancel = function() { + return this.handle_.cancel(); +}; + + +/** + * Focuses the wrapped driver on the window managed by this class. + * @return {!webdriver.promise.Promise} A promise that will be resolved + * when this command has completed. + */ +webdriver.testing.Window.prototype.focus = function() { + return this.driver_.switchTo().window(this.handle_); +}; + + +/** + * Focuses on and closes the managed window. The driver must be + * focused on another window before issuing any further commands. + * @return {!webdriver.promise.Promise} A promise that will be resolved + * when this command has completed. + */ +webdriver.testing.Window.prototype.close = function() { + var self = this; + return this.window_.then(function(win) { + if (win) { + win.close(); + } else { + return self.focus().then(goog.bind(self.driver_.close, self.driver_)); + } + }); +}; + + +/** + * Retrieves the current size of this window. + * @return {!webdriver.promise.Promise} A promise that resolves to the size of + * this window as a {width:number, height:number} object. + */ +webdriver.testing.Window.prototype.getSize = function() { + var driver = this.driver_; + return this.focus().then(function() { + return driver.manage().window().getSize(); + }); +}; + + +/** + * Sets the size of this window. + * @param {number} width The desired width, in pixels. + * @param {number} height The desired height, in pixels. + * @return {!webdriver.promise.Promise} A promise that resolves when the + * command has completed. + */ +webdriver.testing.Window.prototype.setSize = function(width, height) { + var driver = this.driver_; + return this.focus().then(function() { + return driver.manage().window().setSize(width, height); + }); +}; + + +/** + * Retrieves the current position of this window, in pixels relative to the + * upper left corner of the screen. + * @return {!webdriver.promise.Promise} A promise that resolves to the + * position of this window as a {x:number, y:number} object. + */ +webdriver.testing.Window.prototype.getPosition = function() { + var driver = this.driver_; + return this.focus().then(function() { + return driver.manage().window().getPosition(); + }); +}; + + +/** + * Repositions this window. + * @param {number} x The desired horizontal position, in pixels from the left + * side of the screen. + * @param {number} y The desired vertical position, in pixels from the top + * of the screen. + * @return {!webdriver.promise.Promise} A promise that resolves when the + * command has completed. + */ +webdriver.testing.Window.prototype.setPosition = function(x, y) { + var driver = this.driver_; + return this.focus().then(function() { + return driver.manage().window().setPosition(x, y); + }); +}; diff --git a/lib/webdriver/touchsequence.js b/lib/webdriver/touchsequence.js new file mode 100644 index 0000000..a5b302d --- /dev/null +++ b/lib/webdriver/touchsequence.js @@ -0,0 +1,248 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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. + +goog.provide('webdriver.TouchSequence'); + +goog.require('goog.array'); +goog.require('webdriver.Command'); +goog.require('webdriver.CommandName'); + + + +/** + * Class for defining sequences of user touch interactions. Each sequence + * will not be executed until {@link #perform} is called. + * + * Example: + * + * new webdriver.TouchSequence(driver). + * tapAndHold({x: 0, y: 0}). + * move({x: 3, y: 4}). + * release({x: 10, y: 10}). + * perform(); + * + * @param {!webdriver.WebDriver} driver The driver instance to use. + * @constructor + */ +webdriver.TouchSequence = function(driver) { + + /** @private {!webdriver.WebDriver} */ + this.driver_ = driver; + + /** @private {!Array<{description: string, command: !webdriver.Command}>} */ + this.touchActions_ = []; +}; + + +/** + * Schedules an action to be executed each time {@link #perform} is called on + * this instance. + * @param {string} description A description of the command. + * @param {!webdriver.Command} command The command. + * @private + */ +webdriver.TouchSequence.prototype.schedule_ = function(description, command) { + this.touchActions_.push({ + description: description, + command: command + }); +}; + + +/** + * Executes this action sequence. + * @return {!webdriver.promise.Promise} A promise that will be resolved once + * this sequence has completed. + */ +webdriver.TouchSequence.prototype.perform = function() { + // Make a protected copy of the scheduled actions. This will protect against + // users defining additional commands before this sequence is actually + // executed. + var actions = goog.array.clone(this.touchActions_); + var driver = this.driver_; + return driver.controlFlow().execute(function() { + goog.array.forEach(actions, function(action) { + driver.schedule(action.command, action.description); + }); + }, 'TouchSequence.perform'); +}; + + +/** + * Taps an element. + * + * @param {!webdriver.WebElement} elem The element to tap. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.tap = function(elem) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_SINGLE_TAP). + setParameter('element', elem.getRawId()); + + this.schedule_('tap', command); + return this; +}; + + +/** + * Double taps an element. + * + * @param {!webdriver.WebElement} elem The element to double tap. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.doubleTap = function(elem) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_DOUBLE_TAP). + setParameter('element', elem.getRawId()); + + this.schedule_('doubleTap', command); + return this; +}; + + +/** + * Long press on an element. + * + * @param {!webdriver.WebElement} elem The element to long press. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.longPress = function(elem) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_LONG_PRESS). + setParameter('element', elem.getRawId()); + + this.schedule_('longPress', command); + return this; +}; + + +/** + * Touch down at the given location. + * + * @param {{x: number, y: number}} location The location to touch down at. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.tapAndHold = function(location) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_DOWN). + setParameter('x', location.x). + setParameter('y', location.y); + + this.schedule_('tapAndHold', command); + return this; +}; + + +/** + * Move a held {@linkplain #tapAndHold touch} to the specified location. + * + * @param {{x: number, y: number}} location The location to move to. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.move = function(location) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_MOVE). + setParameter('x', location.x). + setParameter('y', location.y); + + this.schedule_('move', command); + return this; +}; + + +/** + * Release a held {@linkplain #tapAndHold touch} at the specified location. + * + * @param {{x: number, y: number}} location The location to release at. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.release = function(location) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_UP). + setParameter('x', location.x). + setParameter('y', location.y); + + this.schedule_('release', command); + return this; +}; + + +/** + * Scrolls the touch screen by the given offset. + * + * @param {{x: number, y: number}} offset The offset to scroll to. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.scroll = function(offset) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_SCROLL). + setParameter('xoffset', offset.x). + setParameter('yoffset', offset.y); + + this.schedule_('scroll', command); + return this; +}; + + +/** + * Scrolls the touch screen, starting on `elem` and moving by the specified + * offset. + * + * @param {!webdriver.WebElement} elem The element where scroll starts. + * @param {{x: number, y: number}} offset The offset to scroll to. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.scrollFromElement = function(elem, offset) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_SCROLL). + setParameter('element', elem.getRawId()). + setParameter('xoffset', offset.x). + setParameter('yoffset', offset.y); + + this.schedule_('scrollFromElement', command); + return this; +}; + + +/** + * Flick, starting anywhere on the screen, at speed xspeed and yspeed. + * + * @param {{xspeed: number, yspeed: number}} speed The speed to flick in each + direction, in pixels per second. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.flick = function(speed) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_FLICK). + setParameter('xspeed', speed.xspeed). + setParameter('yspeed', speed.yspeed); + + this.schedule_('flick', command); + return this; +}; + + +/** + * Flick starting at elem and moving by x and y at specified speed. + * + * @param {!webdriver.WebElement} elem The element where flick starts. + * @param {{x: number, y: number}} offset The offset to flick to. + * @param {number} speed The speed to flick at in pixels per second. + * @return {!webdriver.TouchSequence} A self reference. + */ +webdriver.TouchSequence.prototype.flickElement = function(elem, offset, speed) { + var command = new webdriver.Command(webdriver.CommandName.TOUCH_FLICK). + setParameter('element', elem.getRawId()). + setParameter('xoffset', offset.x). + setParameter('yoffset', offset.y). + setParameter('speed', speed); + + this.schedule_('flickElement', command); + return this; +}; + diff --git a/lib/webdriver/until.js b/lib/webdriver/until.js new file mode 100644 index 0000000..03d24bb --- /dev/null +++ b/lib/webdriver/until.js @@ -0,0 +1,412 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Defines common conditions for use with + * {@link webdriver.WebDriver#wait WebDriver wait}. + * + * Sample usage: + * + * driver.get('http://www.google.com/ncr'); + * + * var query = driver.wait(until.elementLocated(By.name('q'))); + * query.sendKeys('webdriver\n'); + * + * driver.wait(until.titleIs('webdriver - Google Search')); + * + * To define a custom condition, simply call WebDriver.wait with a function + * that will eventually return a truthy-value (neither null, undefined, false, + * 0, or the empty string): + * + * driver.wait(function() { + * return driver.getTitle().then(function(title) { + * return title === 'webdriver - Google Search'; + * }); + * }, 1000); + */ + +goog.provide('webdriver.until'); + +goog.require('bot.ErrorCode'); +goog.require('goog.array'); +goog.require('goog.string'); +goog.require('webdriver.Locator'); + + + +goog.scope(function() { + +var until = webdriver.until; + + +/** + * Defines a condition to + * @param {string} message A descriptive error message. Should complete the + * sentence "Waiting [...]" + * @param {function(!webdriver.WebDriver): OUT} fn The condition function to + * evaluate on each iteration of the wait loop. + * @constructor + * @struct + * @final + * @template OUT + */ +until.Condition = function(message, fn) { + /** @private {string} */ + this.description_ = 'Waiting ' + message; + + /** @type {function(!webdriver.WebDriver): OUT} */ + this.fn = fn; +}; + + +/** @return {string} A description of this condition. */ +until.Condition.prototype.description = function() { + return this.description_; +}; + + +/** + * Creates a condition that will wait until the input driver is able to switch + * to the designated frame. The target frame may be specified as + * + * 1. a numeric index into + * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames) + * for the currently selected frame. + * 2. a {@link webdriver.WebElement}, which must reference a FRAME or IFRAME + * element on the current page. + * 3. a locator which may be used to first locate a FRAME or IFRAME on the + * current page before attempting to switch to it. + * + * Upon successful resolution of this condition, the driver will be left + * focused on the new frame. + * + * @param {!(number|webdriver.WebElement| + * webdriver.Locator|webdriver.By.Hash| + * function(!webdriver.WebDriver): !webdriver.WebElement)} frame + * The frame identifier. + * @return {!until.Condition.} A new condition. + */ +until.ableToSwitchToFrame = function(frame) { + var condition; + if (goog.isNumber(frame) || frame instanceof webdriver.WebElement) { + condition = attemptToSwitchFrames; + } else { + condition = function(driver) { + var locator = + /** @type {!(webdriver.Locator|webdriver.By.Hash|Function)} */(frame); + return driver.findElements(locator).then(function(els) { + if (els.length) { + return attemptToSwitchFrames(driver, els[0]); + } + }); + }; + } + + return new until.Condition('to be able to switch to frame', condition); + + function attemptToSwitchFrames(driver, frame) { + return driver.switchTo().frame(frame).then( + function() { return true; }, + function(e) { + if (e && e.code !== bot.ErrorCode.NO_SUCH_FRAME) { + throw e; + } + }); + } +}; + + +/** + * Creates a condition that waits for an alert to be opened. Upon success, the + * returned promise will be fulfilled with the handle for the opened alert. + * + * @return {!until.Condition.} The new condition. + */ +until.alertIsPresent = function() { + return new until.Condition('for alert to be present', function(driver) { + return driver.switchTo().alert().thenCatch(function(e) { + if (e && e.code !== bot.ErrorCode.NO_SUCH_ALERT) { + throw e; + } + }); + }); +}; + + +/** + * Creates a condition that will wait for the current page's title to match the + * given value. + * + * @param {string} title The expected page title. + * @return {!until.Condition.} The new condition. + */ +until.titleIs = function(title) { + return new until.Condition( + 'for title to be ' + goog.string.quote(title), + function(driver) { + return driver.getTitle().then(function(t) { + return t === title; + }); + }); +}; + + +/** + * Creates a condition that will wait for the current page's title to contain + * the given substring. + * + * @param {string} substr The substring that should be present in the page + * title. + * @return {!until.Condition.} The new condition. + */ +until.titleContains = function(substr) { + return new until.Condition( + 'for title to contain ' + goog.string.quote(substr), + function(driver) { + return driver.getTitle().then(function(title) { + return title.indexOf(substr) !== -1; + }); + }); +}; + + +/** + * Creates a condition that will wait for the current page's title to match the + * given regular expression. + * + * @param {!RegExp} regex The regular expression to test against. + * @return {!until.Condition.} The new condition. + */ +until.titleMatches = function(regex) { + return new until.Condition('for title to match ' + regex, function(driver) { + return driver.getTitle().then(function(title) { + return regex.test(title); + }); + }); +}; + + +/** + * Creates a condition that will loop until an element is + * {@link webdriver.WebDriver#findElement found} with the given locator. + * + * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator + * to use. + * @return {!until.Condition.} The new condition. + */ +until.elementLocated = function(locator) { + locator = webdriver.Locator.checkLocator(locator); + var locatorStr = goog.isFunction(locator) ? 'by function()' : locator + ''; + return new until.Condition('for element to be located ' + locatorStr, + function(driver) { + return driver.findElements(locator).then(function(elements) { + return elements[0]; + }); + }); +}; + + +/** + * Creates a condition that will loop until at least one element is + * {@link webdriver.WebDriver#findElement found} with the given locator. + * + * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator + * to use. + * @return {!until.Condition.>} The new + * condition. + */ +until.elementsLocated = function(locator) { + locator = webdriver.Locator.checkLocator(locator); + var locatorStr = goog.isFunction(locator) ? 'by function()' : locator + ''; + return new until.Condition( + 'for at least one element to be located ' + locatorStr, + function(driver) { + return driver.findElements(locator).then(function(elements) { + return elements.length > 0 ? elements : null; + }); + }); +}; + + +/** + * Creates a condition that will wait for the given element to become stale. An + * element is considered stale once it is removed from the DOM, or a new page + * has loaded. + * + * @param {!webdriver.WebElement} element The element that should become stale. + * @return {!until.Condition.} The new condition. + */ +until.stalenessOf = function(element) { + return new until.Condition('element to become stale', function() { + return element.getTagName().then( + function() { return false; }, + function(e) { + if (e.code === bot.ErrorCode.STALE_ELEMENT_REFERENCE) { + return true; + } + throw e; + }); + }); +}; + + +/** + * Creates a condition that will wait for the given element to become visible. + * + * @param {!webdriver.WebElement} element The element to test. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#isDisplayed + */ +until.elementIsVisible = function(element) { + return new until.Condition('until element is visible', function() { + return element.isDisplayed(); + }); +}; + + +/** + * Creates a condition that will wait for the given element to be in the DOM, + * yet not visible to the user. + * + * @param {!webdriver.WebElement} element The element to test. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#isDisplayed + */ +until.elementIsNotVisible = function(element) { + return new until.Condition('until element is not visible', function() { + return element.isDisplayed().then(function(v) { + return !v; + }); + }); +}; + + +/** + * Creates a condition that will wait for the given element to be enabled. + * + * @param {!webdriver.WebElement} element The element to test. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#isEnabled + */ +until.elementIsEnabled = function(element) { + return new until.Condition('until element is enabled', function() { + return element.isEnabled(); + }); +}; + + +/** + * Creates a condition that will wait for the given element to be disabled. + * + * @param {!webdriver.WebElement} element The element to test. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#isEnabled + */ +until.elementIsDisabled = function(element) { + return new until.Condition('until element is disabled', function() { + return element.isEnabled().then(function(v) { + return !v; + }); + }); +}; + + +/** + * Creates a condition that will wait for the given element to be selected. + * @param {!webdriver.WebElement} element The element to test. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#isSelected + */ +until.elementIsSelected = function(element) { + return new until.Condition('until element is selected', function() { + return element.isSelected(); + }); +}; + + +/** + * Creates a condition that will wait for the given element to be deselected. + * + * @param {!webdriver.WebElement} element The element to test. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#isSelected + */ +until.elementIsNotSelected = function(element) { + return new until.Condition('until element is not selected', function() { + return element.isSelected().then(function(v) { + return !v; + }); + }); +}; + + +/** + * Creates a condition that will wait for the given element's + * {@link webdriver.WebDriver#getText visible text} to match the given + * {@code text} exactly. + * + * @param {!webdriver.WebElement} element The element to test. + * @param {string} text The expected text. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#getText + */ +until.elementTextIs = function(element, text) { + return new until.Condition('until element text is', function() { + return element.getText().then(function(t) { + return t === text; + }); + }); +}; + + +/** + * Creates a condition that will wait for the given element's + * {@link webdriver.WebDriver#getText visible text} to contain the given + * substring. + * + * @param {!webdriver.WebElement} element The element to test. + * @param {string} substr The substring to search for. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#getText + */ +until.elementTextContains = function(element, substr) { + return new until.Condition('until element text contains', function() { + return element.getText().then(function(t) { + return t.indexOf(substr) != -1; + }); + }); +}; + + +/** + * Creates a condition that will wait for the given element's + * {@link webdriver.WebDriver#getText visible text} to match a regular + * expression. + * + * @param {!webdriver.WebElement} element The element to test. + * @param {!RegExp} regex The regular expression to test against. + * @return {!until.Condition.} The new condition. + * @see webdriver.WebDriver#getText + */ +until.elementTextMatches = function(element, regex) { + return new until.Condition('until element text matches', function() { + return element.getText().then(function(t) { + return regex.test(t); + }); + }); +}; +}); // goog.scope diff --git a/lib/webdriver/webdriver.js b/lib/webdriver/webdriver.js index f666fb3..fd78032 100644 --- a/lib/webdriver/webdriver.js +++ b/lib/webdriver/webdriver.js @@ -1,25 +1,31 @@ -// Copyright 2011 Software Freedom Conservancy. All Rights Reserved. +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 heart of the WebDriver JavaScript API. */ goog.provide('webdriver.Alert'); +goog.provide('webdriver.AlertPromise'); +goog.provide('webdriver.FileDetector'); goog.provide('webdriver.UnhandledAlertError'); goog.provide('webdriver.WebDriver'); goog.provide('webdriver.WebElement'); +goog.provide('webdriver.WebElementPromise'); goog.require('bot.Error'); goog.require('bot.ErrorCode'); @@ -31,9 +37,12 @@ goog.require('webdriver.Command'); goog.require('webdriver.CommandName'); goog.require('webdriver.Key'); goog.require('webdriver.Locator'); +goog.require('webdriver.Serializable'); goog.require('webdriver.Session'); +goog.require('webdriver.TouchSequence'); goog.require('webdriver.logging'); goog.require('webdriver.promise'); +goog.require('webdriver.until'); ////////////////////////////////////////////////////////////////////////////// @@ -52,16 +61,15 @@ goog.require('webdriver.promise'); * 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'));
        - *   });
        - * 
        + * + * 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')); + * }); * * @param {!(webdriver.Session|webdriver.promise.Promise)} session Either a * known session or a promise that will be resolved to a session. @@ -81,6 +89,9 @@ webdriver.WebDriver = function(session, executor, opt_flow) { /** @private {!webdriver.promise.ControlFlow} */ this.flow_ = opt_flow || webdriver.promise.controlFlow(); + + /** @private {webdriver.FileDetector} */ + this.fileDetector_ = null; }; @@ -89,13 +100,17 @@ webdriver.WebDriver = function(session, executor, opt_flow) { * @param {!webdriver.CommandExecutor} executor Command executor to use when * querying for session details. * @param {string} sessionId ID of the session to attach to. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver + * commands should execute under. Defaults to the + * {@link webdriver.promise.controlFlow() currently active} control flow. * @return {!webdriver.WebDriver} A new client for the specified session. */ -webdriver.WebDriver.attachToSession = function(executor, sessionId) { +webdriver.WebDriver.attachToSession = function(executor, sessionId, opt_flow) { return webdriver.WebDriver.acquireSession_(executor, new webdriver.Command(webdriver.CommandName.DESCRIBE_SESSION). setParameter('sessionId', sessionId), - 'WebDriver.attachToSession()'); + 'WebDriver.attachToSession()', + opt_flow); }; @@ -105,13 +120,19 @@ webdriver.WebDriver.attachToSession = function(executor, sessionId) { * session with. * @param {!webdriver.Capabilities} desiredCapabilities The desired * capabilities for the new session. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver + * commands should execute under, including the initial session creation. + * Defaults to the {@link webdriver.promise.controlFlow() currently active} + * control flow. * @return {!webdriver.WebDriver} The driver for the newly created session. */ -webdriver.WebDriver.createSession = function(executor, desiredCapabilities) { +webdriver.WebDriver.createSession = function( + executor, desiredCapabilities, opt_flow) { return webdriver.WebDriver.acquireSession_(executor, new webdriver.Command(webdriver.CommandName.NEW_SESSION). setParameter('desiredCapabilities', desiredCapabilities), - 'WebDriver.createSession()'); + 'WebDriver.createSession()', + opt_flow); }; @@ -124,11 +145,16 @@ webdriver.WebDriver.createSession = function(executor, desiredCapabilities) { * @param {!webdriver.Command} command The command to send to fetch the session * details. * @param {string} description A descriptive debug label for this action. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow all driver + * commands should execute under. Defaults to the + * {@link webdriver.promise.controlFlow() currently active} control flow. * @return {!webdriver.WebDriver} A new WebDriver client for the session. * @private */ -webdriver.WebDriver.acquireSession_ = function(executor, command, description) { - var session = webdriver.promise.controlFlow().execute(function() { +webdriver.WebDriver.acquireSession_ = function( + executor, command, description, opt_flow) { + var flow = opt_flow || webdriver.promise.controlFlow(); + var session = flow.execute(function() { return webdriver.WebDriver.executeCommand_(executor, command). then(function(response) { bot.response.checkResponse(response); @@ -136,7 +162,7 @@ webdriver.WebDriver.acquireSession_ = function(executor, command, description) { response['value']); }); }, description); - return new webdriver.WebDriver(session, executor); + return new webdriver.WebDriver(session, executor, flow); }; @@ -144,48 +170,101 @@ webdriver.WebDriver.acquireSession_ = function(executor, command, description) { * Converts an object to its JSON representation in the WebDriver wire protocol. * When converting values of type object, the following steps will be taken: *
          - *
        1. if the object provides a "toWireValue" function, the return value will - * be returned in its fully resolved state (e.g. this function may return - * promise values)
        2. - *
        3. if the object provides a "toJSON" function, the return value of this - * function will be returned
        4. + *
        5. if the object is a WebElement, the return value will be the element's + * server ID + *
        6. if the object is a Serializable, its + * {@link webdriver.Serializable#serialize} function will be invoked and + * this algorithm will recursively be applied to the result + *
        7. if the object provides a "toJSON" function, this algorithm will + * recursively be applied to the result of that function *
        8. otherwise, the value of each key will be recursively converted according - * to the rules above.
        9. + * to the rules above. *
        * * @param {*} obj The object to convert. - * @return {!webdriver.promise.Promise} A promise that will resolve to the + * @return {!webdriver.promise.Promise.} A promise that will resolve to the * input value's JSON representation. * @private - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol */ webdriver.WebDriver.toWireValue_ = function(obj) { - switch (goog.typeOf(obj)) { - case 'array': - return webdriver.promise.fullyResolved( - goog.array.map(/** @type {!Array} */ (obj), - webdriver.WebDriver.toWireValue_)); - case 'object': - if (goog.isFunction(obj.toWireValue)) { - return webdriver.promise.fullyResolved(obj.toWireValue()); + if (webdriver.promise.isPromise(obj)) { + return obj.then(webdriver.WebDriver.toWireValue_); + } + return webdriver.promise.fulfilled(convertValue(obj)); + + function convertValue(value) { + switch (goog.typeOf(value)) { + case 'array': + return convertKeys(value, true); + case 'object': + // NB: WebElement is a Serializable, but we know its serialized form + // is a promise for its wire format. This is a micro optimization to + // avoid creating extra promises by recursing on the promised id. + if (value instanceof webdriver.WebElement) { + return value.getId(); + } + if (value instanceof webdriver.Serializable) { + return webdriver.WebDriver.toWireValue_(value.serialize()); + } + if (goog.isFunction(value.toJSON)) { + return webdriver.WebDriver.toWireValue_(value.toJSON()); + } + if (goog.isNumber(value.nodeType) && goog.isString(value.nodeName)) { + throw new TypeError( + 'Invalid argument type: ' + value.nodeName + + '(' + value.nodeType + ')'); + } + return convertKeys(value, false); + case 'function': + return '' + value; + case 'undefined': + return null; + default: + return value; + } + } + + function convertKeys(obj, isArray) { + var numKeys = isArray ? obj.length : goog.object.getCount(obj); + var ret = isArray ? new Array(numKeys) : {}; + if (!numKeys) { + return webdriver.promise.fulfilled(ret); + } + + var numResolved = 0; + var done = webdriver.promise.defer(); + + // forEach will stop iteration at undefined, where we want to convert + // these to null and keep iterating. + var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) { + var n = arr.length; + for (var i = 0; i < n; i++) { + fn(arr[i], i); } - if (goog.isFunction(obj.toJSON)) { - return webdriver.promise.fulfilled(obj.toJSON()); + }; + + forEachKey(obj, function(value, key) { + if (webdriver.promise.isPromise(value)) { + value.then(webdriver.WebDriver.toWireValue_). + then(setValue, done.reject); + } else { + webdriver.promise.asap(convertValue(value), setValue, done.reject); } - if (goog.isNumber(obj.nodeType) && goog.isString(obj.nodeName)) { - throw Error([ - 'Invalid argument type: ', obj.nodeName, '(', obj.nodeType, ')' - ].join('')); + + function setValue(value) { + ret[key] = value; + maybeFulfill(); + } + }); + + return done.promise; + + function maybeFulfill() { + if (++numResolved === numKeys) { + done.fulfill(ret); } - return webdriver.promise.fullyResolved( - goog.object.map(/** @type {!Object} */ (obj), - webdriver.WebDriver.toWireValue_)); - case 'function': - return webdriver.promise.fulfilled('' + obj); - case 'undefined': - return webdriver.promise.fulfilled(null); - default: - return webdriver.promise.fulfilled(obj); + } } }; @@ -200,7 +279,7 @@ webdriver.WebDriver.toWireValue_ = function(obj) { * parent of any unwrapped {@code webdriver.WebElement} values. * @param {*} value The value to convert. * @return {*} The converted value. - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol * @private */ webdriver.WebDriver.fromWireValue_ = function(driver, value) { @@ -209,8 +288,7 @@ webdriver.WebDriver.fromWireValue_ = function(driver, value) { goog.partial(webdriver.WebDriver.fromWireValue_, driver)); } else if (value && goog.isObject(value) && !goog.isFunction(value)) { if (webdriver.WebElement.ELEMENT_KEY in value) { - value = new webdriver.WebElement(driver, - value[webdriver.WebElement.ELEMENT_KEY]); + value = new webdriver.WebElement(driver, value); } else { value = goog.object.map(/**@type {!Object}*/ (value), goog.partial(webdriver.WebDriver.fromWireValue_, driver)); @@ -230,8 +308,7 @@ webdriver.WebDriver.fromWireValue_ = function(driver, value) { * @private */ webdriver.WebDriver.executeCommand_ = function(executor, command) { - return webdriver.promise.fullyResolved(command.getParameters()). - then(webdriver.WebDriver.toWireValue_). + return webdriver.WebDriver.toWireValue_(command.getParameters()). then(function(parameters) { command.setParameters(parameters); return webdriver.promise.checkedNodeCall( @@ -254,8 +331,9 @@ webdriver.WebDriver.prototype.controlFlow = function() { * {@code webdriver.CommandExecutor}. * @param {!webdriver.Command} command The command to schedule. * @param {string} description A description of the command for debugging. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * the command result. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * with the command result. + * @template T */ webdriver.WebDriver.prototype.schedule = function(command, description) { var self = this; @@ -263,22 +341,47 @@ webdriver.WebDriver.prototype.schedule = function(command, description) { checkHasNotQuit(); command.setParameter('sessionId', this.session_); + // If any of the command parameters are rejected promises, those + // rejections may be reported as unhandled before the control flow + // attempts to execute the command. To ensure parameters errors + // propagate through the command itself, we resolve all of the + // command parameters now, but suppress any errors until the ControlFlow + // actually executes the command. This addresses scenarios like catching + // an element not found error in: + // + // driver.findElement(By.id('foo')).click().thenCatch(function(e) { + // if (e.code === bot.ErrorCode.NO_SUCH_ELEMENT) { + // // Do something. + // } + // }); + var prepCommand = webdriver.WebDriver.toWireValue_(command.getParameters()); + prepCommand.thenCatch(goog.nullFunction); + var flow = this.flow_; + var executor = this.executor_; return flow.execute(function() { // A call to WebDriver.quit() may have been scheduled in the same event // loop as this |command|, which would prevent us from detecting that the // driver has quit above. Therefore, we need to make another quick check. // We still check above so we can fail as early as possible. checkHasNotQuit(); - return webdriver.WebDriver.executeCommand_(self.executor_, command); + + // Retrieve resolved command parameters; any previously suppressed errors + // will now propagate up through the control flow as part of the command + // execution. + return prepCommand.then(function(parameters) { + command.setParameters(parameters); + return webdriver.promise.checkedNodeCall( + goog.bind(executor.execute, executor, command)); + }); }, description).then(function(response) { try { bot.response.checkResponse(response); } catch (ex) { var value = response['value']; - if (ex.code === bot.ErrorCode.MODAL_DIALOG_OPENED) { + if (ex.code === bot.ErrorCode.UNEXPECTED_ALERT_OPEN) { var text = value && value['alert'] ? value['alert']['text'] : ''; - throw new webdriver.UnhandledAlertError(ex.message, + throw new webdriver.UnhandledAlertError(ex.message, text, new webdriver.Alert(self, text)); } throw ex; @@ -296,13 +399,24 @@ webdriver.WebDriver.prototype.schedule = function(command, description) { }; +/** + * Sets the {@linkplain webdriver.FileDetector file detector} that should be + * used with this instance. + * @param {webdriver.FileDetector} detector The detector to use or {@code null}. + */ +webdriver.WebDriver.prototype.setFileDetector = function(detector) { + this.fileDetector_ = detector; +}; + + // ---------------------------------------------------------------------------- // Client command functions: // ---------------------------------------------------------------------------- /** - * @return {!webdriver.promise.Promise} A promise for this client's session. + * @return {!webdriver.promise.Promise.} A promise for this + * client's session. */ webdriver.WebDriver.prototype.getSession = function() { return webdriver.promise.when(this.session_); @@ -310,8 +424,8 @@ webdriver.WebDriver.prototype.getSession = function() { /** - * @return {!webdriver.promise.Promise} A promise that will resolve with the - * this instance's capabilities. + * @return {!webdriver.promise.Promise.} A promise + * that will resolve with the this instance's capabilities. */ webdriver.WebDriver.prototype.getCapabilities = function() { return webdriver.promise.when(this.session_, function(session) { @@ -324,8 +438,8 @@ webdriver.WebDriver.prototype.getCapabilities = function() { * 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. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * the command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the command has completed. */ webdriver.WebDriver.prototype.quit = function() { var result = this.schedule( @@ -343,13 +457,13 @@ webdriver.WebDriver.prototype.quit = function() { * Creates a new action sequence using this driver. The sequence will not be * scheduled for execution until {@link webdriver.ActionSequence#perform} is * called. Example: - *
        
        - *   driver.actions().
        - *       mouseDown(element1).
        - *       mouseMove(element2).
        - *       mouseUp().
        - *       perform();
        - * 
        + * + * driver.actions(). + * mouseDown(element1). + * mouseMove(element2). + * mouseUp(). + * perform(); + * * @return {!webdriver.ActionSequence} A new action sequence for this instance. */ webdriver.WebDriver.prototype.actions = function() { @@ -357,6 +471,23 @@ webdriver.WebDriver.prototype.actions = function() { }; +/** + * Creates a new touch sequence using this driver. The sequence will not be + * scheduled for execution until {@link webdriver.TouchSequence#perform} is + * called. Example: + * + * driver.touchActions(). + * tap(element1). + * doubleTap(element2). + * perform(); + * + * @return {!webdriver.TouchSequence} A new touch sequence for this instance. + */ +webdriver.WebDriver.prototype.touchActions = function() { + return new webdriver.TouchSequence(this); +}; + + /** * 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 @@ -379,29 +510,30 @@ webdriver.WebDriver.prototype.actions = function() { * 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: - *
          - *
        • For a HTML element, the value will resolve to a - * {@code webdriver.WebElement}
        • - *
        • Null and undefined return values will resolve to null
        • - *
        • Booleans, numbers, and strings will resolve as is
        • - *
        • Functions will resolve to their string representation
        • - *
        • For arrays and objects, each member item will be converted according to - * the rules above
        • - *
        + * + * - For a HTML element, the value will resolve to a + * {@link webdriver.WebElement} + * - Null and undefined return values will resolve to null + * - Booleans, numbers, and strings will resolve as is + * - Functions will resolve to their string representation + * - For arrays and objects, each member item will be converted according to + * the rules above * * @param {!(string|Function)} script The script to execute. * @param {...*} var_args The arguments to pass to the script. - * @return {!webdriver.promise.Promise} A promise that will resolve to the + * @return {!webdriver.promise.Promise.} A promise that will resolve to the * scripts return value. + * @template T */ webdriver.WebDriver.prototype.executeScript = function(script, var_args) { if (goog.isFunction(script)) { script = 'return (' + script + ').apply(null, arguments);'; } + var args = arguments.length > 1 ? goog.array.slice(arguments, 1) : []; return this.schedule( new webdriver.Command(webdriver.CommandName.EXECUTE_SCRIPT). setParameter('script', script). - setParameter('args', goog.array.slice(arguments, 1)), + setParameter('args', args), 'WebDriver.executeScript()'); }; @@ -419,71 +551,68 @@ webdriver.WebDriver.prototype.executeScript = function(script, var_args) { * 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 - * {@code 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 - * {@code 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: - *
          - *
        • For a HTML element, the value will resolve to a - * {@code webdriver.WebElement}
        • - *
        • Null and undefined return values will resolve to null
        • - *
        • Booleans, numbers, and strings will resolve as is
        • - *
        • Functions will resolve to their string representation
        • - *
        • For arrays and objects, each member item will be converted according to - * the rules above
        • - *
        - * - * Example #1: Performing a sleep that is synchronized with the currently + * Unlike executing synchronous JavaScript with {@link #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 {@code 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: + * + * - For a HTML element, the value will resolve to a + * {@link webdriver.WebElement} + * - Null and undefined return values will resolve to null + * - Booleans, numbers, and strings will resolve as is + * - Functions will resolve to their string representation + * - For arrays and objects, each member item will be converted according to + * the rules above + * + * __Example #1:__ Performing a sleep that is synchronized with the currently * selected window: - *
        - * 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 #2: Synchronizing a test with an AJAX application: - *
        - * 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');
        - * 
        - * - * 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 + * + * 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 #2:__ Synchronizing a test with an AJAX application: + * + * 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'); + * + * __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. - *
        - * 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']);
        - * });
        - * 
        + * + * 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.responseText); + * } + * } + * xhr.send(''); + * }).then(function(str) { + * console.log(JSON.parse(str)['food']); + * }); * * @param {!(string|Function)} script The script to execute. * @param {...*} var_args The arguments to pass to the script. - * @return {!webdriver.promise.Promise} A promise that will resolve to the + * @return {!webdriver.promise.Promise.} A promise that will resolve to the * scripts return value. + * @template T */ webdriver.WebDriver.prototype.executeAsyncScript = function(script, var_args) { if (goog.isFunction(script)) { @@ -499,17 +628,23 @@ webdriver.WebDriver.prototype.executeAsyncScript = function(script, var_args) { /** * Schedules a command to execute a custom function. - * @param {!Function} fn The function to execute. + * @param {function(...): (T|webdriver.promise.Promise.)} fn The function to + * execute. * @param {Object=} opt_scope The object in whose scope to execute the function. * @param {...*} var_args Any arguments to pass to the function. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * function's result. + * @return {!webdriver.promise.Promise.} A promise that will be resolved' + * with the function's result. + * @template T */ webdriver.WebDriver.prototype.call = function(fn, opt_scope, var_args) { var args = goog.array.slice(arguments, 2); var flow = this.flow_; return flow.execute(function() { return webdriver.promise.fullyResolved(args).then(function(args) { + if (webdriver.promise.isGenerator(fn)) { + args.unshift(fn, opt_scope); + return webdriver.promise.consume.apply(null, args); + } return fn.apply(opt_scope, args); }); }, 'WebDriver.call(' + (fn.name || 'function') + ')'); @@ -517,26 +652,81 @@ webdriver.WebDriver.prototype.call = function(fn, opt_scope, var_args) { /** - * 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. - * @param {function():boolean} fn The function to evaluate as a wait condition. - * @param {number} timeout How long to wait for the condition to be true. + * Schedules a command to wait for a condition to hold. The condition may be + * specified by a {@link webdriver.until.Condition}, as a custom function, or + * as a {@link webdriver.promise.Promise}. + * + * For a {@link webdriver.until.Condition} or function, the wait will repeatedly + * evaluate the condition until it returns a truthy value. If any errors occur + * while evaluating the condition, they will be allowed to propagate. In the + * event a condition returns a {@link webdriver.promise.Promise promise}, the + * polling loop will wait for it to be resolved and use the resolved value for + * whether the condition has been satisified. Note the resolution time for + * a promise is factored into whether a wait has timed out. + * + * *Example:* waiting up to 10 seconds for an element to be present and visible + * on the page. + * + * var button = driver.wait(until.elementLocated(By.id('foo')), 10000); + * button.click(); + * + * This function may also be used to block the command flow on the resolution + * of a {@link webdriver.promise.Promise promise}. When given a promise, the + * command will simply wait for its resolution before completing. A timeout may + * be provided to fail the command if the promise does not resolve before the + * timeout expires. + * + * *Example:* Suppose you have a function, `startTestServer`, that returns a + * promise for when a server is ready for requests. You can block a `WebDriver` + * client on this promise with: + * + * var started = startTestServer(); + * driver.wait(started, 5 * 1000, 'Server should start within 5 seconds'); + * driver.get(getServerUrl()); + * + * @param {!(webdriver.promise.Promise| + * webdriver.until.Condition| + * function(!webdriver.WebDriver): T)} condition The condition to + * wait on, defined as a promise, condition object, or a function to + * evaluate as a condition. + * @param {number=} opt_timeout How long to wait for the condition to be true. * @param {string=} opt_message An optional message to use if the wait times * out. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * wait condition has been satisfied. - */ -webdriver.WebDriver.prototype.wait = function(fn, timeout, opt_message) { - return this.flow_.wait(fn, timeout, opt_message); + * @return {!webdriver.promise.Promise} A promise that will be fulfilled + * with the first truthy value returned by the condition function, or + * rejected if the condition times out. + * @template T + */ +webdriver.WebDriver.prototype.wait = function( + condition, opt_timeout, opt_message) { + if (webdriver.promise.isPromise(condition)) { + return this.flow_.wait( + /** @type {!webdriver.promise.Promise} */(condition), + opt_timeout, opt_message); + } + + var message = opt_message; + var fn = /** @type {!Function} */(condition); + if (condition instanceof webdriver.until.Condition) { + message = message || condition.description(); + fn = condition.fn; + } + + var driver = this; + return this.flow_.wait(function() { + if (webdriver.promise.isGenerator(fn)) { + return webdriver.promise.consume(fn, null, [driver]); + } + return fn(driver); + }, opt_timeout, message); }; /** * Schedules a command to make the driver sleep for the given amount of time. * @param {number} ms The amount of time, in milliseconds, to sleep. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * sleep has finished. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the sleep has finished. */ webdriver.WebDriver.prototype.sleep = function(ms) { return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')'); @@ -545,8 +735,8 @@ webdriver.WebDriver.prototype.sleep = function(ms) { /** * Schedules a command to retrieve they current window handle. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * current window handle. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the current window handle. */ webdriver.WebDriver.prototype.getWindowHandle = function() { return this.schedule( @@ -557,8 +747,8 @@ webdriver.WebDriver.prototype.getWindowHandle = function() { /** * Schedules a command to retrieve the current list of available window handles. - * @return {!webdriver.promise.Promise} A promise that will be resolved with an - * array of window handles. + * @return {!webdriver.promise.Promise.>} A promise that will + * be resolved with an array of window handles. */ webdriver.WebDriver.prototype.getAllWindowHandles = function() { return this.schedule( @@ -572,8 +762,8 @@ webdriver.WebDriver.prototype.getAllWindowHandles = function() { * 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. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * current page source. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the current page source. */ webdriver.WebDriver.prototype.getPageSource = function() { return this.schedule( @@ -584,8 +774,8 @@ webdriver.WebDriver.prototype.getPageSource = function() { /** * Schedules a command to close the current window. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * this command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when this command has completed. */ webdriver.WebDriver.prototype.close = function() { return this.schedule(new webdriver.Command(webdriver.CommandName.CLOSE), @@ -596,8 +786,8 @@ webdriver.WebDriver.prototype.close = function() { /** * Schedules a command to navigate to the given URL. * @param {string} url The fully qualified URL to open. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * document has finished loading. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the document has finished loading. */ webdriver.WebDriver.prototype.get = function(url) { return this.navigate().to(url); @@ -606,8 +796,8 @@ webdriver.WebDriver.prototype.get = function(url) { /** * Schedules a command to retrieve the URL of the current page. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * current URL. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the current URL. */ webdriver.WebDriver.prototype.getCurrentUrl = function() { return this.schedule( @@ -618,8 +808,8 @@ webdriver.WebDriver.prototype.getCurrentUrl = function() { /** * Schedules a command to retrieve the current page's title. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * current page's title. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the current page's title. */ webdriver.WebDriver.prototype.getTitle = function() { return this.schedule(new webdriver.Command(webdriver.CommandName.GET_TITLE), @@ -635,33 +825,31 @@ webdriver.WebDriver.prototype.getTitle = function() { * that the element is present on the page. To test whether an element is * present on the page, use {@link #isElementPresent} instead. * - *

        The search criteria for an element may be defined using one of the + * The search criteria for an element may be defined using one of the * factories in the {@link webdriver.By} namespace, or as a short-hand * {@link 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'});
        - * 
        * - *

        You may also provide a custom locator function, which takes as input + * var e1 = driver.findElement(By.id('foo')); + * var e2 = driver.findElement({id:'foo'}); + * + * You may also provide a custom locator function, which takes as input * this WebDriver instance and returns a {@link 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];
        - *   });
        - * }
        - * 
        - * - *

        When running in the browser, a WebDriver cannot manipulate DOM elements + * + * 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]; + * }); + * } + * + * When running in the browser, a WebDriver cannot manipulate DOM elements * directly; it may do so only through a {@link 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 @@ -680,15 +868,14 @@ webdriver.WebDriver.prototype.findElement = function(locator) { var id; if ('nodeType' in locator && 'ownerDocument' in locator) { var element = /** @type {!Element} */ (locator); - id = this.findDomElement_(element). - then(function(elements) { - if (!elements.length) { - throw new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT, - 'Unable to locate element. Is WebDriver focused on its ' + - 'ownerDocument\'s frame?'); - } - return elements[0]; - }); + id = this.findDomElement_(element).then(function(element) { + if (!element) { + throw new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT, + 'Unable to locate element. Is WebDriver focused on its ' + + 'ownerDocument\'s frame?'); + } + return element; + }); } else { locator = webdriver.Locator.checkLocator(locator); if (goog.isFunction(locator)) { @@ -700,7 +887,7 @@ webdriver.WebDriver.prototype.findElement = function(locator) { id = this.schedule(command, 'WebDriver.findElement(' + locator + ')'); } } - return new webdriver.WebElement(this, id); + return new webdriver.WebElementPromise(this, id); }; @@ -735,8 +922,9 @@ webdriver.WebDriver.prototype.findElementInternal_ = function( * ownerDocument's window+frame. * @param {!Element} element The element to locate. - * @return {!webdriver.promise.Promise} A promise that will be resolved - * with the located WebElement. + * @return {!webdriver.promise.Promise.} A promise that + * will be fulfilled with the located element, or null if the element + * could not be found. * @private */ webdriver.WebDriver.prototype.findDomElement_ = function(element) { @@ -758,26 +946,22 @@ webdriver.WebDriver.prototype.findDomElement_ = function(element) { var element = store[id]; if (!element || element[id] !== id) { - return []; + return null; } - return [element]; + return element; } - return this.executeScript(lookupElement, id). - then(function(value) { - cleanUp(); - if (value.length && !(value[0] instanceof webdriver.WebElement)) { - throw new Error('JS locator script result was not a WebElement'); - } - return value; - }, cleanUp); + /** @type {!webdriver.promise.Promise.} */ + var foundElement = this.executeScript(lookupElement, id); + foundElement.thenFinally(cleanUp); + return foundElement; }; /** * 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 + * 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. * @@ -788,13 +972,14 @@ webdriver.WebDriver.prototype.findDomElement_ = function(element) { * with whether the element is present on the page. */ webdriver.WebDriver.prototype.isElementPresent = function(locatorOrElement) { - var findElement = - ('nodeType' in locatorOrElement && 'ownerDocument' in locatorOrElement) ? - this.findDomElement_(/** @type {!Element} */ (locatorOrElement)) : - this.findElements.apply(this, arguments); - return findElement.then(function(result) { - return !!result.length; - }); + if ('nodeType' in locatorOrElement && 'ownerDocument' in locatorOrElement) { + return this.findDomElement_(/** @type {!Element} */ (locatorOrElement)). + then(function(result) { return !!result; }); + } else { + return this.findElements.apply(this, arguments).then(function(result) { + return !!result.length; + }); + } }; @@ -855,8 +1040,8 @@ webdriver.WebDriver.prototype.findElementsInternal_ = function( *

      • The screenshot of the entire display containing the browser * * - * @return {!webdriver.promise.Promise} A promise that will be resolved to the - * screenshot as a base-64 encoded PNG. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved to the screenshot as a base-64 encoded PNG. */ webdriver.WebDriver.prototype.takeScreenshot = function() { return this.schedule(new webdriver.Command(webdriver.CommandName.SCREENSHOT), @@ -907,8 +1092,8 @@ webdriver.WebDriver.Navigation = function(driver) { /** * Schedules a command to navigate to a new URL. * @param {string} url The URL to navigate to. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * URL has been loaded. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the URL has been loaded. */ webdriver.WebDriver.Navigation.prototype.to = function(url) { return this.driver_.schedule( @@ -920,8 +1105,8 @@ webdriver.WebDriver.Navigation.prototype.to = function(url) { /** * Schedules a command to move backwards in the browser history. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * navigation event has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the navigation event has completed. */ webdriver.WebDriver.Navigation.prototype.back = function() { return this.driver_.schedule( @@ -932,8 +1117,8 @@ webdriver.WebDriver.Navigation.prototype.back = function() { /** * Schedules a command to move forwards in the browser history. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * navigation event has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the navigation event has completed. */ webdriver.WebDriver.Navigation.prototype.forward = function() { return this.driver_.schedule( @@ -944,8 +1129,8 @@ webdriver.WebDriver.Navigation.prototype.forward = function() { /** * Schedules a command to refresh the current page. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * navigation event has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the navigation event has completed. */ webdriver.WebDriver.Navigation.prototype.refresh = function() { return this.driver_.schedule( @@ -967,6 +1152,21 @@ webdriver.WebDriver.Options = function(driver) { }; +/** + * A JSON description of a browser cookie. + * @typedef {{ + * name: string, + * value: string, + * path: (string|undefined), + * domain: (string|undefined), + * secure: (boolean|undefined), + * expiry: (number|undefined) + * }} + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object + */ +webdriver.WebDriver.Options.Cookie; + + /** * Schedules a command to add a cookie. * @param {string} name The cookie name. @@ -976,8 +1176,8 @@ webdriver.WebDriver.Options = function(driver) { * @param {boolean=} opt_isSecure Whether the cookie is secure. * @param {(number|!Date)=} opt_expiry When the cookie expires. If specified as * a number, should be in milliseconds since midnight, January 1, 1970 UTC. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * cookie has been added to the page. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the cookie has been added to the page. */ webdriver.WebDriver.Options.prototype.addCookie = function( name, value, opt_path, opt_domain, opt_isSecure, opt_expiry) { @@ -1026,8 +1226,8 @@ webdriver.WebDriver.Options.prototype.addCookie = function( /** * Schedules a command to delete all cookies visible to the current page. - * @return {!webdriver.promise.Promise} A promise that will be resolved when all - * cookies have been deleted. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when all cookies have been deleted. */ webdriver.WebDriver.Options.prototype.deleteAllCookies = function() { return this.driver_.schedule( @@ -1041,8 +1241,8 @@ webdriver.WebDriver.Options.prototype.deleteAllCookies = function() { * a no-op if there is no cookie with the given name visible to the current * page. * @param {string} name The name of the cookie to delete. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * cookie has been deleted. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the cookie has been deleted. */ webdriver.WebDriver.Options.prototype.deleteCookie = function(name) { return this.driver_.schedule( @@ -1056,9 +1256,10 @@ webdriver.WebDriver.Options.prototype.deleteCookie = function(name) { * 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. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * cookies visible to the current page. - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object + * @return {!webdriver.promise.Promise.< + * !Array.>} A promise that will be + * resolved with the cookies visible to the current page. + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object */ webdriver.WebDriver.Options.prototype.getCookies = function() { return this.driver_.schedule( @@ -1072,9 +1273,10 @@ webdriver.WebDriver.Options.prototype.getCookies = function() { * if there is no such cookie. The cookie will be returned as a JSON object as * described by the WebDriver wire protocol. * @param {string} name The name of the cookie to retrieve. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * named cookie, or {@code null} if there is no such cookie. - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object + * @return {!webdriver.promise.Promise.} A + * promise that will be resolved with the named cookie, or {@code null} + * if there is no such cookie. + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#cookie-json-object */ webdriver.WebDriver.Options.prototype.getCookie = function(name) { return this.getCookies().then(function(cookies) { @@ -1128,23 +1330,23 @@ webdriver.WebDriver.Timeouts = function(driver) { /** * Specifies the amount of time the driver should wait when searching for an * element if it is not immediately present. - *

        + * * When searching for a single element, the driver should poll the page * until the element has been found, or this timeout expires before failing - * with a {@code bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching + * with a {@link 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. * * @param {number} ms The amount of time to wait, in milliseconds. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * implicit wait timeout has been set. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the implicit wait timeout has been set. */ webdriver.WebDriver.Timeouts.prototype.implicitlyWait = function(ms) { return this.driver_.schedule( @@ -1160,8 +1362,8 @@ webdriver.WebDriver.Timeouts.prototype.implicitlyWait = function(ms) { * equal to 0, the script will be allowed to run indefinitely. * * @param {number} ms The amount of time to wait, in milliseconds. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * script timeout has been set. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the script timeout has been set. */ webdriver.WebDriver.Timeouts.prototype.setScriptTimeout = function(ms) { return this.driver_.schedule( @@ -1175,8 +1377,8 @@ webdriver.WebDriver.Timeouts.prototype.setScriptTimeout = function(ms) { * 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. * @param {number} ms The amount of time to wait, in milliseconds. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * the timeout has been set. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the timeout has been set. */ webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout = function(ms) { return this.driver_.schedule( @@ -1203,8 +1405,9 @@ webdriver.WebDriver.Window = function(driver) { /** * Retrieves the window's current position, relative to the top left corner of * the screen. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * window's position in the form of a {x:number, y:number} object literal. + * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that + * will be resolved with the window's position in the form of a + * {x:number, y:number} object literal. */ webdriver.WebDriver.Window.prototype.getPosition = function() { return this.driver_.schedule( @@ -1220,8 +1423,8 @@ webdriver.WebDriver.Window.prototype.getPosition = function() { * of the screen. * @param {number} y The desired vertical position, relative to the top of the * of the screen. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the command has completed. */ webdriver.WebDriver.Window.prototype.setPosition = function(x, y) { return this.driver_.schedule( @@ -1235,9 +1438,9 @@ webdriver.WebDriver.Window.prototype.setPosition = function(x, y) { /** * Retrieves the window's current size. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * window's size in the form of a {width:number, height:number} object - * literal. + * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A + * promise that will be resolved with the window's size in the form of a + * {width:number, height:number} object literal. */ webdriver.WebDriver.Window.prototype.getSize = function() { return this.driver_.schedule( @@ -1251,8 +1454,8 @@ webdriver.WebDriver.Window.prototype.getSize = function() { * Resizes the current window. * @param {number} width The desired window width. * @param {number} height The desired window height. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the command has completed. */ webdriver.WebDriver.Window.prototype.setSize = function(width, height) { return this.driver_.schedule( @@ -1266,8 +1469,8 @@ webdriver.WebDriver.Window.prototype.setSize = function(width, height) { /** * Maximizes the current window. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the command has completed. */ webdriver.WebDriver.Window.prototype.maximize = function() { return this.driver_.schedule( @@ -1292,11 +1495,10 @@ webdriver.WebDriver.Logs = function(driver) { /** * Fetches available log entries for the given type. * - *

        Note that log buffers are reset after each call, meaning that - * available log entries correspond to those entries not yet returned for a - * given log type. In practice, this means that this call will return the - * available log entries since the last call, or from the start of the - * session. + * Note that log buffers are reset after each call, meaning that available + * log entries correspond to those entries not yet returned for a given log + * type. In practice, this means that this call will return the available log + * entries since the last call, or from the start of the session. * * @param {!webdriver.logging.Type} type The desired log type. * @return {!webdriver.promise.Promise.>} A @@ -1312,7 +1514,8 @@ webdriver.WebDriver.Logs.prototype.get = function(type) { return goog.array.map(entries, function(entry) { if (!(entry instanceof webdriver.logging.Entry)) { return new webdriver.logging.Entry( - entry['level'], entry['message'], entry['timestamp']); + entry['level'], entry['message'], entry['timestamp'], + entry['type']); } return entry; }); @@ -1349,21 +1552,21 @@ webdriver.WebDriver.TargetLocator = function(driver) { * Schedules a command retrieve the {@code document.activeElement} element on * the current document, or {@code document.body} if activeElement is not * available. - * @return {!webdriver.WebElement} The active element. + * @return {!webdriver.WebElementPromise} The active element. */ webdriver.WebDriver.TargetLocator.prototype.activeElement = function() { var id = this.driver_.schedule( new webdriver.Command(webdriver.CommandName.GET_ACTIVE_ELEMENT), 'WebDriver.switchTo().activeElement()'); - return new webdriver.WebElement(this.driver_, id); + return new webdriver.WebElementPromise(this.driver_, id); }; /** * Schedules a command to switch focus of all future commands to the first frame * on the page. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * driver has changed focus to the default content. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the driver has changed focus to the default content. */ webdriver.WebDriver.TargetLocator.prototype.defaultContent = function() { return this.driver_.schedule( @@ -1376,20 +1579,22 @@ webdriver.WebDriver.TargetLocator.prototype.defaultContent = function() { /** * Schedules a command to switch the focus of all future commands to another * frame on the page. - *

        + * * If the frame is specified by a number, the command will switch to the frame - * by its (zero-based) index into the {@code window.frames} collection. - *

        + * by its (zero-based) index into + * [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames). + * * 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 {@code bot.ErrorCode.NO_SUCH_FRAME} error. + * with a {@link bot.ErrorCode.NO_SUCH_FRAME} error. + * * @param {string|number} nameOrIndex The frame locator. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * driver has changed focus to the specified frame. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the driver has changed focus to the specified frame. */ webdriver.WebDriver.TargetLocator.prototype.frame = function(nameOrIndex) { return this.driver_.schedule( @@ -1402,14 +1607,15 @@ webdriver.WebDriver.TargetLocator.prototype.frame = function(nameOrIndex) { /** * Schedules a command to switch the focus of all future commands to another * window. Windows may be specified by their {@code window.name} attribute or - * by its handle (as returned by {@code webdriver.WebDriver#getWindowHandles}). - *

        + * by its handle (as returned by {@link webdriver.WebDriver#getWindowHandles}). + * * If the specificed window can not be found, the deferred result will errback - * with a {@code bot.ErrorCode.NO_SUCH_WINDOW} error. + * with a {@link bot.ErrorCode.NO_SUCH_WINDOW} error. + * * @param {string} nameOrHandle The name or window handle of the window to * switch focus to. - * @return {!webdriver.promise.Promise} A promise that will be resolved when the - * driver has changed focus to the specified window. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the driver has changed focus to the specified window. */ webdriver.WebDriver.TargetLocator.prototype.window = function(nameOrHandle) { return this.driver_.schedule( @@ -1421,15 +1627,18 @@ webdriver.WebDriver.TargetLocator.prototype.window = function(nameOrHandle) { /** * Schedules a command to change focus to the active alert dialog. This command - * will return a {@link bot.ErrorCode.NO_MODAL_DIALOG_OPEN} error if a modal - * dialog is not currently open. - * @return {!webdriver.Alert} The open alert. + * will return a {@link bot.ErrorCode.NO_SUCH_ALERT} error if an alert dialog + * is not currently open. + * @return {!webdriver.AlertPromise} The open alert. */ webdriver.WebDriver.TargetLocator.prototype.alert = function() { var text = this.driver_.schedule( new webdriver.Command(webdriver.CommandName.GET_ALERT_TEXT), 'WebDriver.switchTo().alert()'); - return new webdriver.Alert(this.driver_, text); + var driver = this.driver_; + return new webdriver.AlertPromise(driver, text.then(function(text) { + return new webdriver.Alert(driver, text); + })); }; @@ -1467,75 +1676,52 @@ webdriver.Key.chord = function(var_args) { /** * Represents a DOM element. WebElements can be found by searching from the - * document root using a {@code webdriver.WebDriver} instance, or by searching - * under another {@code webdriver.WebElement}: - *

        
        - *   driver.get('http://www.google.com');
        - *   var searchForm = driver.findElement(By.tagName('form'));
        - *   var searchBox = searchForm.findElement(By.name('q'));
        - *   searchBox.sendKeys('webdriver');
        - * 
        + * document root using a {@link webdriver.WebDriver} instance, or by searching + * under another WebElement: + * + * driver.get('http://www.google.com'); + * var searchForm = driver.findElement(By.tagName('form')); + * var searchBox = searchForm.findElement(By.name('q')); + * searchBox.sendKeys('webdriver'); * * 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.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');
        - *   });
        - * 
        + * + * 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'); + * }); * * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this * element. - * @param {!(string|webdriver.promise.Promise)} id Either the opaque ID for the - * underlying DOM element assigned by the server, or a promise that will - * resolve to that ID or another WebElement. + * @param {!(webdriver.promise.Promise.| + * webdriver.WebElement.Id)} id The server-assigned opaque ID for the + * underlying DOM element. * @constructor - * @extends {webdriver.promise.Deferred} + * @extends {webdriver.Serializable.} */ webdriver.WebElement = function(driver, id) { - webdriver.promise.Deferred.call(this, null, driver.controlFlow()); + webdriver.Serializable.call(this); - /** - * The parent WebDriver instance for this element. - * @private {!webdriver.WebDriver} - */ + /** @private {!webdriver.WebDriver} */ this.driver_ = driver; - // This class is responsible for resolving itself; delete the resolve and - // reject methods so they may not be accessed by consumers of this class. - var fulfill = goog.partial(this.fulfill, this); - var reject = this.reject; - delete this.promise; - delete this.fulfill; - delete this.reject; - - /** - * A promise that resolves to the JSON representation of this WebElement's - * ID, as defined by the WebDriver wire protocol. - * @private {!webdriver.promise.Promise} - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol - */ - this.id_ = webdriver.promise.when(id, function(id) { - if (id instanceof webdriver.WebElement) { - return id.id_; - } else if (goog.isDef(id[webdriver.WebElement.ELEMENT_KEY])) { - return id; - } + /** @private {!webdriver.promise.Promise.} */ + this.id_ = id instanceof webdriver.promise.Promise ? + id : webdriver.promise.fulfilled(id); +}; +goog.inherits(webdriver.WebElement, webdriver.Serializable); - var json = {}; - json[webdriver.WebElement.ELEMENT_KEY] = id; - return json; - }); - // This WebElement should not be resolved until its ID has been - // fully resolved. - this.id_.then(fulfill, reject); -}; -goog.inherits(webdriver.WebElement, webdriver.promise.Deferred); +/** + * Wire protocol definition of a WebElement ID. + * @typedef {{ELEMENT: string}} + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol + */ +webdriver.WebElement.Id; /** @@ -1551,14 +1737,15 @@ webdriver.WebElement.ELEMENT_KEY = 'ELEMENT'; * Compares to WebElements for equality. * @param {!webdriver.WebElement} a A WebElement. * @param {!webdriver.WebElement} b A WebElement. - * @return {!webdriver.promise.Promise} A promise that will be resolved to - * whether the two WebElements are equal. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved to whether the two WebElements are equal. */ webdriver.WebElement.equals = function(a, b) { if (a == b) { return webdriver.promise.fulfilled(true); } - return webdriver.promise.fullyResolved([a.id_, b.id_]).then(function(ids) { + var ids = [a.getId(), b.getId()]; + return webdriver.promise.all(ids).then(function(ids) { // If the two element's have the same ID, they should be considered // equal. Otherwise, they may still be equivalent, but we'll need to // ask the server to check for us. @@ -1567,10 +1754,10 @@ webdriver.WebElement.equals = function(a, b) { return true; } - var command = new webdriver.Command( - webdriver.CommandName.ELEMENT_EQUALS); - command.setParameter('other', b); - return a.schedule_(command, 'webdriver.WebElement.equals()'); + var command = new webdriver.Command(webdriver.CommandName.ELEMENT_EQUALS); + command.setParameter('id', ids[0]); + command.setParameter('other', ids[1]); + return a.driver_.schedule(command, 'webdriver.WebElement.equals()'); }); }; @@ -1584,65 +1771,84 @@ webdriver.WebElement.prototype.getDriver = function() { /** - * @return {!webdriver.promise.Promise} A promise that resolves to this - * element's JSON representation as defined by the WebDriver wire protocol. - * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol + * @return {!webdriver.promise.Promise.} A promise + * that resolves to this element's JSON representation as defined by the + * WebDriver wire protocol. + * @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol */ -webdriver.WebElement.prototype.toWireValue = function() { +webdriver.WebElement.prototype.getId = function() { return this.id_; }; +/** + * Returns the raw ID string ID for this element. + * @return {!webdriver.promise.Promise} A promise that resolves to this + * element's raw ID as a string value. + * @package + */ +webdriver.WebElement.prototype.getRawId = function() { + return this.getId().then(function(value) { + return value['ELEMENT']; + }); +}; + + +/** @override */ +webdriver.WebElement.prototype.serialize = function() { + return this.getId(); +}; + + /** * 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. * @param {!webdriver.Command} command The command to schedule. * @param {string} description A description of the command for debugging. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * the command result. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * with the command result. + * @template T * @see webdriver.WebDriver.prototype.schedule * @private */ webdriver.WebElement.prototype.schedule_ = function(command, description) { - command.setParameter('id', this.id_); + command.setParameter('id', this.getId()); return this.driver_.schedule(command, description); }; /** * Schedule a command to find a descendant of this element. If the element - * cannot be found, a {@code bot.ErrorCode.NO_SUCH_ELEMENT} result will + * cannot be found, a {@link bot.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 {@code #isElementPresent} instead. + * element is present on the page, use {@link #isElementPresent} instead. * - *

        The search criteria for an element may be defined using one of the + * The search criteria for an element may be defined using one of the * factories in the {@link webdriver.By} namespace, or as a short-hand * {@link 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'});
        - * 
        * - *

        You may also provide a custom locator function, which takes as input + * var e1 = element.findElement(By.id('foo')); + * var e2 = element.findElement({id:'foo'}); + * + * You may also provide a custom locator function, which takes as input * this WebDriver instance and returns a {@link 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];
        - *   });
        - * }
        - * 
        + * + * 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]; + * }); + * } * * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The * locator strategy to use when searching for the element. @@ -1662,7 +1868,7 @@ webdriver.WebElement.prototype.findElement = function(locator) { setParameter('value', locator.value); id = this.schedule_(command, 'WebElement.findElement(' + locator + ')'); } - return new webdriver.WebElement(this.driver_, id); + return new webdriver.WebElementPromise(this.driver_, id); }; @@ -1707,8 +1913,8 @@ webdriver.WebElement.prototype.findElements = function(locator) { /** * Schedules a command to click on this element. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * the click command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the click command has completed. */ webdriver.WebElement.prototype.click = function() { return this.schedule_( @@ -1720,64 +1926,93 @@ webdriver.WebElement.prototype.click = function() { /** * Schedules a command to type a sequence on the DOM element represented by this * instance. - *

        + * * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is * processed in the keysequence, that key state is toggled until one of the * following occurs: - *

          - *
        • The modifier key is encountered again in the sequence. At this point the - * state of the key is toggled (along with the appropriate keyup/down events). - *
        • - *
        • The {@code 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");
          - * 
        • - *
        • The end of the keysequence is encountered. When there are no more keys - * to type, all depressed modifier keys are released (with accompanying keyup - * events). - *
        • - *
        - * Note: On browsers where native keyboard events are not yet - * supported (e.g. Firefox on OS X), key events will be synthesized. Special + * + * - The modifier key is encountered again in the sequence. At this point the + * state of the key is toggled (along with the appropriate keyup/down events). + * - The {@link 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"); + * + * - The end of the keysequence is encountered. When there are no more keys + * to type, all depressed modifier keys are released (with accompanying keyup + * events). + * + * If this element is a file input ({@code }), the + * specified key sequence should specify the path to the file to attach to + * the element. This is analgous to the user clicking "Browse..." and entering + * the path into the file select dialog. + * + * var form = driver.findElement(By.css('form')); + * var element = form.findElement(By.css('input[type=file]')); + * element.sendKeys('/path/to/file.txt'); + * form.submit(); + * + * For uploads to function correctly, the entered path must reference a file + * on the _browser's_ machine, not the local machine running this script. When + * running against a remote Selenium server, a {@link webdriver.FileDetector} + * may be used to transparently copy files to the remote machine before + * attempting to upload them in the browser. + * + * __Note:__ On browsers where native keyboard events are not 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. * - * @param {...string} var_args The sequence of keys to - * type. All arguments will be joined into a single sequence (var_args is - * permitted for convenience). - * @return {!webdriver.promise.Promise} A promise that will be resolved when all - * keys have been typed. + * @param {...(string|!webdriver.promise.Promise)} var_args The sequence + * of keys to type. All arguments will be joined into a single sequence. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when all keys have been typed. */ webdriver.WebElement.prototype.sendKeys = function(var_args) { // Coerce every argument to a string. This protects us from users that // ignore the jsdoc and give us a number (which ends up causing problems on // the server, which requires strings). - var keys = webdriver.promise.fullyResolved(goog.array.slice(arguments, 0)). - then(function(args) { - return goog.array.map(goog.array.slice(args, 0), function(key) { - return key + ''; - }); + var keys = webdriver.promise.all(goog.array.slice(arguments, 0)). + then(function(keys) { + return goog.array.map(keys, String); }); - return this.schedule_( - new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT). - setParameter('value', keys), - 'WebElement.sendKeys(' + keys + ')'); + if (!this.driver_.fileDetector_) { + return this.schedule_( + new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT). + setParameter('value', keys), + 'WebElement.sendKeys()'); + } + + // Suppress unhandled rejection errors until the flow executes the command. + keys.thenCatch(goog.nullFunction); + + var element = this; + return this.driver_.flow_.execute(function() { + return keys.then(function(keys) { + return element.driver_.fileDetector_ + .handleFile(element.driver_, keys.join('')); + }).then(function(keys) { + return element.schedule_( + new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT). + setParameter('value', [keys]), + 'WebElement.sendKeys()'); + }); + }, 'WebElement.sendKeys()'); }; /** * Schedules a command to query for the tag/node name of this element. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * element's tag name. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the element's tag name. */ webdriver.WebElement.prototype.getTagName = function() { return this.schedule_( @@ -1792,14 +2027,14 @@ webdriver.WebElement.prototype.getTagName = function() { * 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)). - *

        - * Warning: the value returned will be as the browser interprets it, so + * + * _Warning:_ the value returned will be as the browser interprets it, so * it may be tricky to form a proper assertion. * * @param {string} cssStyleProperty The name of the CSS style property to look * up. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * requested CSS value. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the requested CSS value. */ webdriver.WebElement.prototype.getCssValue = function(cssStyleProperty) { var name = webdriver.CommandName.GET_ELEMENT_VALUE_OF_CSS_PROPERTY; @@ -1821,23 +2056,23 @@ webdriver.WebElement.prototype.getCssValue = function(cssStyleProperty) { * 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, + * 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 + * Finally, the following commonly mis-capitalized attribute/property names * are evaluated as expected: - *

          - *
        • "class" - *
        • "readonly" - *
        + * + * - "class" + * - "readonly" + * * @param {string} attributeName The name of the attribute to query. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * attribute's value. The returned value will always be either a string or - * null. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the attribute's value. The returned value will always be + * either a string or null. */ webdriver.WebElement.prototype.getAttribute = function(attributeName) { return this.schedule_( @@ -1850,8 +2085,8 @@ webdriver.WebElement.prototype.getAttribute = function(attributeName) { /** * Get the visible (i.e. not hidden by CSS) innerText of this element, including * sub-elements, without any leading or trailing whitespace. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * element's visible text. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the element's visible text. */ webdriver.WebElement.prototype.getText = function() { return this.schedule_( @@ -1863,8 +2098,9 @@ webdriver.WebElement.prototype.getText = function() { /** * Schedules a command to compute the size of this element's bounding box, in * pixels. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * element's size as a {@code {width:number, height:number}} object. + * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A + * promise that will be resolved with the element's size as a + * {@code {width:number, height:number}} object. */ webdriver.WebElement.prototype.getSize = function() { return this.schedule_( @@ -1875,8 +2111,9 @@ webdriver.WebElement.prototype.getSize = function() { /** * Schedules a command to compute the location of this element in page space. - * @return {!webdriver.promise.Promise} A promise that will be resolved to the - * element's location as a {@code {x:number, y:number}} object. + * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that + * will be resolved to the element's location as a + * {@code {x:number, y:number}} object. */ webdriver.WebElement.prototype.getLocation = function() { return this.schedule_( @@ -1888,8 +2125,8 @@ webdriver.WebElement.prototype.getLocation = function() { /** * Schedules a command to query whether the DOM element represented by this * instance is enabled, as dicted by the {@code disabled} attribute. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * whether this element is currently enabled. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with whether this element is currently enabled. */ webdriver.WebElement.prototype.isEnabled = function() { return this.schedule_( @@ -1900,8 +2137,8 @@ webdriver.WebElement.prototype.isEnabled = function() { /** * Schedules a command to query whether this element is selected. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * whether this element is currently selected. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with whether this element is currently selected. */ webdriver.WebElement.prototype.isSelected = function() { return this.schedule_( @@ -1914,8 +2151,8 @@ webdriver.WebElement.prototype.isSelected = function() { * 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. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * the form has been submitted. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the form has been submitted. */ webdriver.WebElement.prototype.submit = function() { return this.schedule_( @@ -1928,8 +2165,8 @@ webdriver.WebElement.prototype.submit = function() { * Schedules a command to clear the {@code value} of this element. This command * has no effect if the underlying DOM element is neither a text INPUT element * nor a TEXTAREA element. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * the element has been cleared. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when the element has been cleared. */ webdriver.WebElement.prototype.clear = function() { return this.schedule_( @@ -1940,8 +2177,8 @@ webdriver.WebElement.prototype.clear = function() { /** * Schedules a command to test whether this element is currently displayed. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * whether this element is currently visible on the page. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with whether this element is currently visible on the page. */ webdriver.WebElement.prototype.isDisplayed = function() { return this.schedule_( @@ -1952,8 +2189,8 @@ webdriver.WebElement.prototype.isDisplayed = function() { /** * Schedules a command to retrieve the outer HTML of this element. - * @return {!webdriver.promise.Promise} A promise that will be resolved with - * the element's outer HTML. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the element's outer HTML. */ webdriver.WebElement.prototype.getOuterHtml = function() { return this.driver_.executeScript(function() { @@ -1971,8 +2208,8 @@ webdriver.WebElement.prototype.getOuterHtml = function() { /** * Schedules a command to retrieve the inner HTML of this element. - * @return {!webdriver.promise.Promise} A promise that will be resolved with the - * element's inner HTML. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved with the element's inner HTML. */ webdriver.WebElement.prototype.getInnerHtml = function() { return this.driver_.executeScript('return arguments[0].innerHTML', this); @@ -1980,6 +2217,59 @@ webdriver.WebElement.prototype.getInnerHtml = function() { +/** + * WebElementPromise is a promise that will be fulfilled with a WebElement. + * This serves as a forward proxy on WebElement, allowing calls to be + * scheduled without directly on this instance before the underlying + * WebElement has been fulfilled. In other words, the following two statements + * are equivalent: + * + * driver.findElement({id: 'my-button'}).click(); + * driver.findElement({id: 'my-button'}).then(function(el) { + * return el.click(); + * }); + * + * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this + * element. + * @param {!webdriver.promise.Promise.} el A promise + * that will resolve to the promised element. + * @constructor + * @extends {webdriver.WebElement} + * @implements {webdriver.promise.Thenable.} + * @final + */ +webdriver.WebElementPromise = function(driver, el) { + webdriver.WebElement.call(this, driver, {'ELEMENT': 'unused'}); + + /** @override */ + this.cancel = goog.bind(el.cancel, el); + + /** @override */ + this.isPending = goog.bind(el.isPending, el); + + /** @override */ + this.then = goog.bind(el.then, el); + + /** @override */ + this.thenCatch = goog.bind(el.thenCatch, el); + + /** @override */ + this.thenFinally = goog.bind(el.thenFinally, el); + + /** + * Defers returning the element ID until the wrapped WebElement has been + * resolved. + * @override + */ + this.getId = function() { + return el.then(function(el) { + return el.getId(); + }); + }; +}; +goog.inherits(webdriver.WebElementPromise, webdriver.WebElement); + + /** * Represents a modal dialog such as {@code alert}, {@code confirm}, or * {@code prompt}. Provides functions to retrieve the message displayed with @@ -1987,40 +2277,23 @@ webdriver.WebElement.prototype.getInnerHtml = function() { * case of {@code prompt}). * @param {!webdriver.WebDriver} driver The driver controlling the browser this * alert is attached to. - * @param {!(string|webdriver.promise.Promise)} text Either the message text - * displayed with this alert, or a promise that will be resolved to said - * text. + * @param {string} text The message text displayed with this alert. * @constructor - * @extends {webdriver.promise.Deferred} */ webdriver.Alert = function(driver, text) { - goog.base(this, null, driver.controlFlow()); - /** @private {!webdriver.WebDriver} */ this.driver_ = driver; - // This class is responsible for resolving itself; delete the resolve and - // reject methods so they may not be accessed by consumers of this class. - var fulfill = goog.partial(this.fulfill, this); - var reject = this.reject; - delete this.promise; - delete this.fulfill; - delete this.reject; - - /** @private {!webdriver.promise.Promise} */ + /** @private {!webdriver.promise.Promise.} */ this.text_ = webdriver.promise.when(text); - - // Make sure this instance is resolved when its displayed text is. - this.text_.then(fulfill, reject); }; -goog.inherits(webdriver.Alert, webdriver.promise.Deferred); /** * Retrieves the message text displayed with this alert. For instance, if the * alert were opened with alert("hello"), then this would return "hello". - * @return {!webdriver.promise.Promise} A promise that will be resolved to the - * text displayed with this alert. + * @return {!webdriver.promise.Promise.} A promise that will be + * resolved to the text displayed with this alert. */ webdriver.Alert.prototype.getText = function() { return this.text_; @@ -2029,8 +2302,8 @@ webdriver.Alert.prototype.getText = function() { /** * Accepts this alert. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * this command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when this command has completed. */ webdriver.Alert.prototype.accept = function() { return this.driver_.schedule( @@ -2041,8 +2314,8 @@ webdriver.Alert.prototype.accept = function() { /** * Dismisses this alert. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * this command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when this command has completed. */ webdriver.Alert.prototype.dismiss = function() { return this.driver_.schedule( @@ -2056,8 +2329,8 @@ webdriver.Alert.prototype.dismiss = function() { * the underlying alert does not support response text (e.g. window.alert and * window.confirm). * @param {string} text The text to set. - * @return {!webdriver.promise.Promise} A promise that will be resolved when - * this command has completed. + * @return {!webdriver.promise.Promise.} A promise that will be resolved + * when this command has completed. */ webdriver.Alert.prototype.sendKeys = function(text) { return this.driver_.schedule( @@ -2068,16 +2341,103 @@ webdriver.Alert.prototype.sendKeys = function(text) { +/** + * AlertPromise is a promise that will be fulfilled with an Alert. This promise + * serves as a forward proxy on an Alert, allowing calls to be scheduled + * directly on this instance before the underlying Alert has been fulfilled. In + * other words, the following two statements are equivalent: + * + * driver.switchTo().alert().dismiss(); + * driver.switchTo().alert().then(function(alert) { + * return alert.dismiss(); + * }); + * + * @param {!webdriver.WebDriver} driver The driver controlling the browser this + * alert is attached to. + * @param {!webdriver.promise.Thenable.} alert A thenable + * that will be fulfilled with the promised alert. + * @constructor + * @extends {webdriver.Alert} + * @implements {webdriver.promise.Thenable.} + * @final + */ +webdriver.AlertPromise = function(driver, alert) { + webdriver.Alert.call(this, driver, 'unused'); + + /** @override */ + this.cancel = goog.bind(alert.cancel, alert); + + /** @override */ + this.isPending = goog.bind(alert.isPending, alert); + + /** @override */ + this.then = goog.bind(alert.then, alert); + + /** @override */ + this.thenCatch = goog.bind(alert.thenCatch, alert); + + /** @override */ + this.thenFinally = goog.bind(alert.thenFinally, alert); + + /** + * Defer returning text until the promised alert has been resolved. + * @override + */ + this.getText = function() { + return alert.then(function(alert) { + return alert.getText(); + }); + }; + + /** + * Defers action until the alert has been located. + * @override + */ + this.accept = function() { + return alert.then(function(alert) { + return alert.accept(); + }); + }; + + /** + * Defers action until the alert has been located. + * @override + */ + this.dismiss = function() { + return alert.then(function(alert) { + return alert.dismiss(); + }); + }; + + /** + * Defers action until the alert has been located. + * @override + */ + this.sendKeys = function(text) { + return alert.then(function(alert) { + return alert.sendKeys(text); + }); + }; +}; +goog.inherits(webdriver.AlertPromise, webdriver.Alert); + + + /** * An error returned to indicate that there is an unhandled modal dialog on the * current page. * @param {string} message The error message. + * @param {string} text The text displayed with the unhandled alert. * @param {!webdriver.Alert} alert The alert handle. * @constructor * @extends {bot.Error} */ -webdriver.UnhandledAlertError = function(message, alert) { - goog.base(this, bot.ErrorCode.MODAL_DIALOG_OPENED, message); +webdriver.UnhandledAlertError = function(message, text, alert) { + webdriver.UnhandledAlertError.base( + this, 'constructor', bot.ErrorCode.UNEXPECTED_ALERT_OPEN, message); + + /** @private {string} */ + this.text_ = text; /** @private {!webdriver.Alert} */ this.alert_ = alert; @@ -2086,8 +2446,47 @@ goog.inherits(webdriver.UnhandledAlertError, bot.Error); /** - * @return {!webdriver.Alert} The open alert. + * @return {string} The text displayed with the unhandled alert. */ -webdriver.UnhandledAlertError.prototype.getAlert = function() { - return this.alert_; +webdriver.UnhandledAlertError.prototype.getAlertText = function() { + return this.text_; }; + + + +/** + * Used with {@link webdriver.WebElement#sendKeys WebElement#sendKeys} on file + * input elements ({@code }) to detect when the entered key + * sequence defines the path to a file. + * + * By default, {@linkplain webdriver.WebElement WebElement's} will enter all + * key sequences exactly as entered. You may set a + * {@linkplain webdriver.WebDriver#setFileDetector file detector} on the parent + * WebDriver instance to define custom behavior for handling file elements. Of + * particular note is the {@link selenium-webdriver/remote.FileDetector}, which + * should be used when running against a remote + * [Selenium Server](http://docs.seleniumhq.org/download/). + */ +webdriver.FileDetector = goog.defineClass(null, { + /** @constructor */ + constructor: function() {}, + + /** + * Handles the file specified by the given path, preparing it for use with + * the current browser. If the path does not refer to a valid file, it will + * be returned unchanged, otherwisee a path suitable for use with the current + * browser will be returned. + * + * This default implementation is a no-op. Subtypes may override this + * function for custom tailored file handling. + * + * @param {!webdriver.WebDriver} driver The driver for the current browser. + * @param {string} path The path to process. + * @return {!webdriver.promise.Promise} A promise for the processed + * file path. + * @package + */ + handleFile: function(driver, path) { + return webdriver.promise.fulfilled(path); + } +}); diff --git a/net/index.js b/net/index.js index 801ec28..4142ebc 100644 --- a/net/index.js +++ b/net/index.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -41,7 +43,8 @@ function getAddress(loopback, opt_family) { var interfaces; if (loopback) { - interfaces = [getLoInterface()]; + var lo = getLoInterface(); + interfaces = lo ? [lo] : null; } interfaces = interfaces || os.networkInterfaces(); for (var key in interfaces) { diff --git a/net/portprober.js b/net/portprober.js index ee57f12..5181167 100644 --- a/net/portprober.js +++ b/net/portprober.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -60,7 +62,7 @@ function findSystemPortRange() { /** * Executes a command and returns its output if it succeeds. * @param {string} cmd The command to execute. - * @return {!webdriver.promise.Promise} A promise that will resolve + * @return {!webdriver.promise.Promise.} A promise that will resolve * with the command's stdout data. */ function execute(cmd) { @@ -78,7 +80,7 @@ function execute(cmd) { /** * Computes the ephemeral port range for a Unix-like system. - * @return {!webdriver.promise.Promise<{min: number, max: number}>} A promise + * @return {!webdriver.promise.Promise.<{min: number, max: number}>} A promise * that will resolve with the ephemeral port range on the current system. */ function findUnixPortRange() { @@ -105,7 +107,7 @@ function findUnixPortRange() { /** * Computes the ephemeral port range for a Windows system. - * @return {!webdriver.promise.Promise<{min: number, max: number}>} A promise + * @return {!webdriver.promise.Promise.<{min: number, max: number}>} A promise * that will resolve with the ephemeral port range on the current system. */ function findWindowsPortRange() { diff --git a/opera.js b/opera.js new file mode 100644 index 0000000..e2579c2 --- /dev/null +++ b/opera.js @@ -0,0 +1,499 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Defines a {@linkplain Driver WebDriver} client for the + * Opera web browser (v26+). Before using this module, you must download the + * latest OperaDriver + * [release](https://github.com/operasoftware/operachromiumdriver/releases) and + * ensure it can be found on your system + * [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). + * + * There are three primary classes exported by this module: + * + * 1. {@linkplain ServiceBuilder}: configures the + * {@link selenium-webdriver/remote.DriverService remote.DriverService} + * that manages the + * [OperaDriver](https://github.com/operasoftware/operachromiumdriver) + * child process. + * + * 2. {@linkplain Options}: defines configuration options for each new Opera + * session, such as which {@linkplain Options#setProxy proxy} to use, + * what {@linkplain Options#addExtensions extensions} to install, or + * what {@linkplain Options#addArguments command-line switches} to use when + * starting the browser. + * + * 3. {@linkplain Driver}: the WebDriver client; each new instance will control + * a unique browser session with a clean user profile (unless otherwise + * configured through the {@link Options} class). + * + * By default, every Opera session will use a single driver service, which is + * started the first time a {@link Driver} instance is created and terminated + * when this process exits. The default service will inherit its environment + * from the current process and direct all output to /dev/null. You may obtain + * a handle to this default service using + * {@link #getDefaultService getDefaultService()} and change its configuration + * with {@link #setDefaultService setDefaultService()}. + * + * You may also create a {@link Driver} with its own driver service. This is + * useful if you need to capture the server's log output for a specific session: + * + * var opera = require('selenium-webdriver/opera'); + * + * var service = new opera.ServiceBuilder() + * .loggingTo('/my/log/file.txt') + * .enableVerboseLogging() + * .build(); + * + * var options = new opera.Options(); + * // configure browser options ... + * + * var driver = new opera.Driver(options, service); + * + * Users should only instantiate the {@link Driver} class directly when they + * need a custom driver service configuration (as shown above). For normal + * operation, users should start Opera using the + * {@link selenium-webdriver.Builder}. + */ + +'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 OperaDriver executable. + * @type {string} + * @const + */ +var OPERADRIVER_EXE = + process.platform === 'win32' ? 'operadriver.exe' : 'operadriver'; + + +/** + * Creates {@link remote.DriverService} instances that manages an + * [OperaDriver](https://github.com/operasoftware/operachromiumdriver) + * server in a child process. + * + * @param {string=} opt_exe Path to the server executable to use. If omitted, + * the builder will attempt to locate the operadriver on the current + * PATH. + * @throws {Error} If provided executable does not exist, or the operadriver + * cannot be found on the PATH. + * @constructor + */ +var ServiceBuilder = function(opt_exe) { + /** @private {string} */ + this.exe_ = opt_exe || io.findInPath(OPERADRIVER_EXE, true); + if (!this.exe_) { + throw Error( + 'The OperaDriver could not be found on the current PATH. Please ' + + 'download the latest version of the OperaDriver from ' + + 'https://github.com/operasoftware/operachromiumdriver/releases and ' + + 'ensure it can be found on your PATH.'); + } + + if (!fs.existsSync(this.exe_)) { + throw Error('File does not exist: ' + this.exe_); + } + + /** @private {!Array.} */ + this.args_ = []; + this.stdio_ = 'ignore'; +}; + + +/** @private {number} */ +ServiceBuilder.prototype.port_ = 0; + + +/** @private {(string|!Array.)} */ +ServiceBuilder.prototype.stdio_ = 'ignore'; + + +/** @private {Object.} */ +ServiceBuilder.prototype.env_ = null; + + +/** + * Sets the port to start the OperaDriver on. + * @param {number} port The port to use, or 0 for any free port. + * @return {!ServiceBuilder} A self reference. + * @throws {Error} If the port is invalid. + */ +ServiceBuilder.prototype.usingPort = function(port) { + if (port < 0) { + throw Error('port must be >= 0: ' + port); + } + this.port_ = port; + return this; +}; + + +/** + * Sets the path of the log file the driver should log to. If a log file is + * not specified, the driver will log to stderr. + * @param {string} path Path of the log file to use. + * @return {!ServiceBuilder} A self reference. + */ +ServiceBuilder.prototype.loggingTo = function(path) { + this.args_.push('--log-path=' + path); + return this; +}; + + +/** + * Enables verbose logging. + * @return {!ServiceBuilder} A self reference. + */ +ServiceBuilder.prototype.enableVerboseLogging = function() { + this.args_.push('--verbose'); + return this; +}; + + +/** + * Silence sthe drivers output. + * @return {!ServiceBuilder} A self reference. + */ +ServiceBuilder.prototype.silent = function() { + this.args_.push('--silent'); + return this; +}; + + +/** + * Defines the stdio configuration for the driver service. See + * {@code child_process.spawn} for more information. + * @param {(string|!Array.)} config The + * configuration to use. + * @return {!ServiceBuilder} A self reference. + */ +ServiceBuilder.prototype.setStdio = function(config) { + this.stdio_ = config; + return this; +}; + + +/** + * Defines the environment to start the server under. This settings will be + * inherited by every browser session started by the server. + * @param {!Object.} env The environment to use. + * @return {!ServiceBuilder} A self reference. + */ +ServiceBuilder.prototype.withEnvironment = function(env) { + this.env_ = env; + return this; +}; + + +/** + * Creates a new DriverService using this instance's current configuration. + * @return {remote.DriverService} A new driver service using this instance's + * current configuration. + * @throws {Error} If the driver exectuable was not specified and a default + * could not be found on the current PATH. + */ +ServiceBuilder.prototype.build = function() { + var port = this.port_ || portprober.findFreePort(); + var args = this.args_.concat(); // Defensive copy. + + return new remote.DriverService(this.exe_, { + loopback: true, + port: port, + args: webdriver.promise.when(port, function(port) { + return args.concat('--port=' + port); + }), + env: this.env_, + stdio: this.stdio_ + }); +}; + + +/** @type {remote.DriverService} */ +var defaultService = null; + + +/** + * Sets the default service to use for new OperaDriver instances. + * @param {!remote.DriverService} service The service to use. + * @throws {Error} If the default service is currently running. + */ +function setDefaultService(service) { + if (defaultService && defaultService.isRunning()) { + throw Error( + 'The previously configured OperaDriver service is still running. ' + + 'You must shut it down before you may adjust its configuration.'); + } + defaultService = service; +} + + +/** + * Returns the default OperaDriver service. If such a service has not been + * configured, one will be constructed using the default configuration for + * a OperaDriver executable found on the system PATH. + * @return {!remote.DriverService} The default OperaDriver service. + */ +function getDefaultService() { + if (!defaultService) { + defaultService = new ServiceBuilder().build(); + } + return defaultService; +} + + +/** + * @type {string} + * @const + */ +var OPTIONS_CAPABILITY_KEY = 'chromeOptions'; + + +/** + * Class for managing {@link Driver OperaDriver} specific options. + * @constructor + * @extends {webdriver.Serializable} + */ +var Options = function() { + webdriver.Serializable.call(this); + + /** @private {!Array.} */ + this.args_ = []; + + /** @private {!Array.<(string|!Buffer)>} */ + this.extensions_ = []; +}; +util.inherits(Options, webdriver.Serializable); + + +/** + * Extracts the OperaDriver specific options from the given capabilities + * object. + * @param {!webdriver.Capabilities} capabilities The capabilities object. + * @return {!Options} The OperaDriver options. + */ +Options.fromCapabilities = function(capabilities) { + var options; + var o = capabilities.get(OPTIONS_CAPABILITY_KEY); + if (o instanceof Options) { + options = o; + } else if (o) { + options. + addArguments(o.args || []). + addExtensions(o.extensions || []). + setOperaBinaryPath(o.binary); + } else { + options = new Options; + } + + if (capabilities.has(webdriver.Capability.PROXY)) { + options.setProxy(capabilities.get(webdriver.Capability.PROXY)); + } + + if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) { + options.setLoggingPrefs( + capabilities.get(webdriver.Capability.LOGGING_PREFS)); + } + + return options; +}; + + +/** + * Add additional command line arguments to use when launching the Opera + * 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". + * @param {...(string|!Array.)} var_args The arguments to add. + * @return {!Options} A self reference. + */ +Options.prototype.addArguments = function(var_args) { + this.args_ = this.args_.concat.apply(this.args_, arguments); + return this; +}; + + +/** + * Add additional extensions to install when launching Opera. Each extension + * should be specified as the path to the packed CRX file, or a Buffer for an + * extension. + * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The + * extensions to add. + * @return {!Options} A self reference. + */ +Options.prototype.addExtensions = function(var_args) { + this.extensions_ = this.extensions_.concat.apply( + this.extensions_, arguments); + return this; +}; + + +/** + * Sets the path to the Opera binary to use. On Mac OS X, this path should + * reference the actual Opera executable, not just the application binary. The + * binary path be absolute or relative to the operadriver server executable, but + * it must exist on the machine that will launch Opera. + * + * @param {string} path The path to the Opera binary to use. + * @return {!Options} A self reference. + */ +Options.prototype.setOperaBinaryPath = function(path) { + this.binary_ = path; + return this; +}; + + +/** + * Sets the logging preferences for the new session. + * @param {!webdriver.logging.Preferences} prefs The logging preferences. + * @return {!Options} A self reference. + */ +Options.prototype.setLoggingPrefs = function(prefs) { + this.logPrefs_ = prefs; + return this; +}; + + +/** + * Sets the proxy settings for the new session. + * @param {webdriver.ProxyConfig} proxy The proxy configuration to use. + * @return {!Options} A self reference. + */ +Options.prototype.setProxy = function(proxy) { + this.proxy_ = proxy; + return this; +}; + + +/** + * Converts this options instance to a {@link webdriver.Capabilities} object. + * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge + * these options into, if any. + * @return {!webdriver.Capabilities} The capabilities. + */ +Options.prototype.toCapabilities = function(opt_capabilities) { + var capabilities = opt_capabilities || webdriver.Capabilities.opera(); + capabilities. + set(webdriver.Capability.PROXY, this.proxy_). + set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_). + set(OPTIONS_CAPABILITY_KEY, this); + return capabilities; +}; + + +/** + * Converts this instance to its JSON wire protocol representation. Note this + * function is an implementation not intended for general use. + * @return {{args: !Array., + * binary: (string|undefined), + * detach: boolean, + * extensions: !Array.<(string|!webdriver.promise.Promise.))>, + * localState: (Object|undefined), + * logPath: (string|undefined), + * prefs: (Object|undefined)}} The JSON wire protocol representation + * of this instance. + * @override + */ +Options.prototype.serialize = function() { + var json = { + args: this.args_, + extensions: this.extensions_.map(function(extension) { + if (Buffer.isBuffer(extension)) { + return extension.toString('base64'); + } + return webdriver.promise.checkedNodeCall( + fs.readFile, extension, 'base64'); + }) + }; + if (this.binary_) { + json.binary = this.binary_; + } + if (this.logFile_) { + json.logPath = this.logFile_; + } + if (this.prefs_) { + json.prefs = this.prefs_; + } + + return json; +}; + + +/** + * Creates a new WebDriver client for Opera. + * + * @param {(webdriver.Capabilities|Options)=} opt_config The configuration + * options. + * @param {remote.DriverService=} opt_service The session to use; will use + * the {@link getDefaultService default service} by default. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or + * {@code null} to use the currently active flow. + * @constructor + * @extends {webdriver.WebDriver} + */ +var Driver = function(opt_config, opt_service, opt_flow) { + var service = opt_service || getDefaultService(); + var executor = executors.createExecutor(service.start()); + + var capabilities = + opt_config instanceof Options ? opt_config.toCapabilities() : + (opt_config || webdriver.Capabilities.opera()); + + // On Linux, the OperaDriver does not look for Opera on the PATH, so we + // must explicitly find it. See: operachromiumdriver #9. + if (process.platform === 'linux') { + var options = Options.fromCapabilities(capabilities); + if (!options.binary_) { + options.setOperaBinaryPath(io.findInPath('opera', true)); + } + capabilities = options.toCapabilities(capabilities); + } + + var driver = webdriver.WebDriver.createSession( + executor, capabilities, opt_flow); + + webdriver.WebDriver.call( + this, driver.getSession(), executor, driver.controlFlow()); +}; +util.inherits(Driver, webdriver.WebDriver); + + +/** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ +Driver.prototype.setFileDetector = function() { +}; + + +// PUBLIC API + + +exports.Driver = Driver; +exports.Options = Options; +exports.ServiceBuilder = ServiceBuilder; +exports.getDefaultService = getDefaultService; +exports.setDefaultService = setDefaultService; diff --git a/package.json b/package.json index f2e2a65..13df439 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "browserstack-webdriver", - "version": "2.41.1", - "description": "BrowserStack WebDriver JavaScript bindings with keep alive support", + "version": "2.48.0", + "description": "Browserstack Webdriver Javascript bindings with keep alive support", + "license": "Apache-2.0", "keywords": [ "automation", "selenium", @@ -19,23 +20,24 @@ "url": "https://github.com/browserstack/selenium-webdriver-nodejs" }, "engines": { - "node": ">= 0.8.x" + "node": ">= 0.12.x" + }, + "dependencies": { + "adm-zip": "0.4.4", + "rimraf": "^2.2.8", + "tmp": "0.0.24", + "ws": "^0.8.0", + "xml2js": "0.4.4", + "keep-alive-agent": "*" }, "devDependencies": { - "mocha": "~1.10.0" + "express": "^4.11.2", + "mocha": ">= 1.21.x", + "multer": "^0.1.7", + "promises-aplus-tests": "^2.1.0", + "serve-index": "^1.6.1" }, "scripts": { - "test": "node_modules/mocha/bin/mocha -R list --recursive test" - }, - "readme": "# browserstack-webdriver\n\n## Installation\n\nInstall the latest published version using `npm`:\n\n npm install browserstack-webdriver\n\nIn addition to the npm package, you will to download the WebDriver\nimplementations you wish to utilize. As of 2.34.0, `browserstack-webdriver`\nnatively supports the [ChromeDriver](http://chromedriver.storage.googleapis.com/index.html).\nSimply download a copy and make sure it can be found on your `PATH`. The other\ndrivers (e.g. Firefox, Internet Explorer, and Safari), still require the\n[standalone Selenium server](http://selenium-release.storage.googleapis.com/index.html).\n\n### Running the tests\n\nTo run the tests, you will need to download a copy of the\n[ChromeDriver](http://chromedriver.storage.googleapis.com/index.html) and make\nsure it can be found on your `PATH`.\n\n npm test browserstack-webdriver\n\nTo run the tests against multiple browsers, download the\n[Selenium server](http://selenium-release.storage.googleapis.com/index.html) and\nspecify its location through the `SELENIUM_SERVER_JAR` environment variable.\nYou can use the `SELENIUM_BROWSER` environment variable to define a\ncomma-separated list of browsers you wish to test against. For example:\n\n export SELENIUM_SERVER_JAR=path/to/selenium-server-standalone-2.33.0.jar\n SELENIUM_BROWSER=chrome,firefox npm test browserstack-webdriver\n\n## Usage\n\n\n var webdriver = require('browserstack-webdriver');\n\n var driver = new webdriver.Builder().\n withCapabilities(webdriver.Capabilities.chrome()).\n build();\n\n driver.get('http://www.google.com');\n driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');\n driver.findElement(webdriver.By.name('btnG')).click();\n driver.wait(function() {\n return driver.getTitle().then(function(title) {\n return title === 'webdriver - Google Search';\n });\n }, 1000);\n\n driver.quit();\n\n## Documentation\n\nFull documentation is available on the [Selenium project wiki](http://code.google.com/p/selenium/wiki/WebDriverJs \"User guide\").\n\n## Issues\n\nPlease report any issues using the [Selenium issue tracker](https://code.google.com/p/selenium/issues/list).\n\n## License\n\nCopyright 2009-2014 Software Freedom Conservancy\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n", - "readmeFilename": "README.md", - "_id": "browserstack-webdriver@2.41.0", - "dist": { - "shasum": "d59dd6297c13b821711bdd8775afa7dffef78ef4" - }, - "_from": "browserstack-webdriver@", - "_resolved": "https://registry.npmjs.org/browserstack-webdriver/-/browserstack-webdriver-2.41.0.tgz", - "dependencies" : { - "keep-alive-agent": "*" + "test": "node_modules/.bin/mocha --harmony -t 600000 --recursive test" } } diff --git a/phantomjs.js b/phantomjs.js index bee3572..b0cf1a4 100644 --- a/phantomjs.js +++ b/phantomjs.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -19,8 +21,8 @@ var fs = require('fs'), util = require('util'); var webdriver = require('./index'), - LogLevel = webdriver.logging.LevelName, executors = require('./executors'), + http = require('./http'), io = require('./io'), portprober = require('./net/portprober'), remote = require('./remote'); @@ -59,6 +61,15 @@ var CLI_ARGS_CAPABILITY = 'phantomjs.cli.args'; var DEFAULT_LOG_FILE = 'phantomjsdriver.log'; +/** + * Custom command names supported by PhantomJS. + * @enum {string} + */ +var Command = { + EXECUTE_PHANTOM_SCRIPT: 'executePhantomScript' +}; + + /** * Finds the PhantomJS executable. * @param {string=} opt_exe Path to the executable to use. @@ -85,30 +96,57 @@ function findExecutable(opt_exe) { /** * Maps WebDriver logging level name to those recognised by PhantomJS. - * @type {!Object.} + * @type {!Object.} * @const */ var WEBDRIVER_TO_PHANTOMJS_LEVEL = (function() { var map = {}; - map[LogLevel.ALL] = map[LogLevel.DEBUG] = 'DEBUG'; - map[LogLevel.INFO] = 'INFO'; - map[LogLevel.WARNING] = 'WARN'; - map[LogLevel.SEVERE] = map[LogLevel.OFF] = 'ERROR'; + map[webdriver.logging.Level.ALL.name] = 'DEBUG'; + map[webdriver.logging.Level.DEBUG.name] = 'DEBUG'; + map[webdriver.logging.Level.INFO.name] = 'INFO'; + map[webdriver.logging.Level.WARNING.name] = 'WARN'; + map[webdriver.logging.Level.SEVERE.name] = 'ERROR'; return map; })(); /** - * Creates a new PhantomJS WebDriver client. + * Creates a command executor with support for PhantomJS' custom commands. + * @param {!webdriver.promise.Promise} url The server's URL. + * @return {!webdriver.CommandExecutor} The new command executor. + */ +function createExecutor(url) { + return new executors.DeferredExecutor(url.then(function(url) { + var client = new http.HttpClient(url); + var executor = new http.Executor(client); + + executor.defineCommand( + Command.EXECUTE_PHANTOM_SCRIPT, + 'POST', '/session/:sessionId/phantom/execute'); + + return executor; + })); +} + +/** + * Creates a new WebDriver client for PhantomJS. + * * @param {webdriver.Capabilities=} opt_capabilities The desired capabilities. - * @return {!webdriver.WebDriver} A new WebDriver instance. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or + * {@code null} to use the currently active flow. + * @constructor + * @extends {webdriver.WebDriver} */ -function createDriver(opt_capabilities) { +var Driver = function(opt_capabilities, opt_flow) { var capabilities = opt_capabilities || webdriver.Capabilities.phantomjs(); var exe = findExecutable(capabilities.get(BINARY_PATH_CAPABILITY)); var args = ['--webdriver-logfile=' + DEFAULT_LOG_FILE]; var logPrefs = capabilities.get(webdriver.Capability.LOGGING_PREFS); + if (logPrefs instanceof webdriver.logging.Preferences) { + logPrefs = logPrefs.toJSON(); + } + if (logPrefs && logPrefs[webdriver.logging.Type.DRIVER]) { var level = WEBDRIVER_TO_PHANTOMJS_LEVEL[ logPrefs[webdriver.logging.Type.DRIVER]]; @@ -148,17 +186,80 @@ function createDriver(opt_capabilities) { }) }); - var executor = executors.createExecutor(service.start()); - var driver = webdriver.WebDriver.createSession(executor, capabilities); - var boundQuit = driver.quit.bind(driver); - driver.quit = function() { + var executor = createExecutor(service.start()); + var driver = webdriver.WebDriver.createSession( + executor, capabilities, opt_flow); + + webdriver.WebDriver.call( + this, driver.getSession(), executor, driver.controlFlow()); + + var boundQuit = this.quit.bind(this); + + /** @override */ + this.quit = function() { return boundQuit().thenFinally(service.kill.bind(service)); }; - return driver; -} +}; +util.inherits(Driver, webdriver.WebDriver); -// PUBLIC API +/** + * This function is a no-op as file detectors are not supported by this + * implementation. + * @override + */ +Driver.prototype.setFileDetector = function() { +}; + +/** + * Executes a PhantomJS fragment. This method is similar to + * {@link #executeScript}, except it exposes the + * PhantomJS API to the injected + * script. + * + *

        The injected script will execute in the context of PhantomJS's + * {@code page} variable. If a page has not been loaded before calling this + * method, one will be created.

        + * + *

        Be sure to wrap callback definitions in a try/catch block, as failures + * may cause future WebDriver calls to fail.

        + * + *

        Certain callbacks are used by GhostDriver (the PhantomJS WebDriver + * implementation) and overriding these may cause the script to fail. It is + * recommended that you check for existing callbacks before defining your own. + *

        + * + * As with {@link #executeScript}, the injected script may be defined as + * a string for an anonymous function body (e.g. "return 123;"), or as a + * function. If a function is provided, it will be decompiled to its original + * source. Note that injecting functions is provided as a convenience to + * simplify defining complex scripts. Care must be taken that the function + * only references variables that will be defined in the page's scope and + * that the function does not override {@code Function.prototype.toString} + * (overriding toString() will interfere with how the function is + * decompiled. + * + * @param {(string|!Function)} script The script to execute. + * @param {...*} var_args The arguments to pass to the script. + * @return {!webdriver.promise.Promise} A promise that resolve to the + * script's return value. + * @template T + */ +Driver.prototype.executePhantomJS = function(script, args) { + if (typeof script === 'function') { + script = 'return (' + script + ').apply(this, arguments);'; + } + var args = arguments.length > 1 + ? Array.prototype.slice.call(arguments, 1) : []; + return this.schedule( + new webdriver.Command(Command.EXECUTE_PHANTOM_SCRIPT) + .setParameter('script', script) + .setParameter('args', args), + 'Driver.executePhantomJS()'); +}; + + +// PUBLIC API -exports.createDriver = createDriver; +exports.Driver = Driver; diff --git a/phantomjsdriver.log b/phantomjsdriver.log deleted file mode 100644 index 7c32424..0000000 --- a/phantomjsdriver.log +++ /dev/null @@ -1,34 +0,0 @@ -[INFO - 2014-03-28T16:52:45.192Z] GhostDriver - Main - running on port 40413 -[INFO - 2014-03-28T16:52:45.244Z] Session [62a32f40-b699-11e3-9b9a-37c865d8e725] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34","webSecurityEnabled":true} -[INFO - 2014-03-28T16:52:45.244Z] Session [62a32f40-b699-11e3-9b9a-37c865d8e725] - page.customHeaders: - {} -[INFO - 2014-03-28T16:52:45.244Z] Session [62a32f40-b699-11e3-9b9a-37c865d8e725] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"1.9.7","driverName":"ghostdriver","driverVersion":"1.1.0","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}} -[INFO - 2014-03-28T16:52:45.244Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 62a32f40-b699-11e3-9b9a-37c865d8e725 -[INFO - 2014-03-28T16:52:52.718Z] GhostDriver - Main - running on port 36577 -[INFO - 2014-03-28T16:52:52.759Z] Session [671dbae0-b699-11e3-9b78-c337d2fb87e4] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34","webSecurityEnabled":true} -[INFO - 2014-03-28T16:52:52.759Z] Session [671dbae0-b699-11e3-9b78-c337d2fb87e4] - page.customHeaders: - {} -[INFO - 2014-03-28T16:52:52.759Z] Session [671dbae0-b699-11e3-9b78-c337d2fb87e4] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"1.9.7","driverName":"ghostdriver","driverVersion":"1.1.0","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}} -[INFO - 2014-03-28T16:52:52.759Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 671dbae0-b699-11e3-9b78-c337d2fb87e4 -[ERROR - 2014-03-28T16:52:53.640Z] WebElementLocator - _handleLocateCommand - Element(s) NOT Found: GAVE UP. Search Stop Time: 1396025573627 -[ERROR - 2014-03-28T16:52:56.439Z] WebElementLocator - _handleLocateCommand - Element(s) NOT Found: GAVE UP. Search Stop Time: 1396025576431 -[ERROR - 2014-03-28T16:52:56.881Z] WebElementLocator - _handleLocateCommand - Element(s) NOT Found: GAVE UP. Search Stop Time: 1396025576875 -[ERROR - 2014-03-28T16:52:57.221Z] WebElementLocator - _handleLocateCommand - Element(s) NOT Found: GAVE UP. Search Stop Time: 1396025577164 -[INFO - 2014-03-28T16:52:59.078Z] GhostDriver - Main - running on port 50797 -[INFO - 2014-03-28T16:52:59.112Z] Session [6ae74600-b699-11e3-a324-653123da115f] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34","webSecurityEnabled":true} -[INFO - 2014-03-28T16:52:59.112Z] Session [6ae74600-b699-11e3-a324-653123da115f] - page.customHeaders: - {} -[INFO - 2014-03-28T16:52:59.113Z] Session [6ae74600-b699-11e3-a324-653123da115f] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"1.9.7","driverName":"ghostdriver","driverVersion":"1.1.0","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}} -[INFO - 2014-03-28T16:52:59.113Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 6ae74600-b699-11e3-a324-653123da115f -[INFO - 2014-03-28T16:53:01.703Z] GhostDriver - Main - running on port 33436 -[INFO - 2014-03-28T16:53:01.748Z] Session [6c797ec0-b699-11e3-9707-815ca604fa62] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34","webSecurityEnabled":true} -[INFO - 2014-03-28T16:53:01.748Z] Session [6c797ec0-b699-11e3-9707-815ca604fa62] - page.customHeaders: - {} -[INFO - 2014-03-28T16:53:01.748Z] Session [6c797ec0-b699-11e3-9707-815ca604fa62] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"1.9.7","driverName":"ghostdriver","driverVersion":"1.1.0","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}} -[INFO - 2014-03-28T16:53:01.749Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 6c797ec0-b699-11e3-9707-815ca604fa62 -[INFO - 2014-03-28T16:53:02.488Z] GhostDriver - Main - running on port 59129 -[INFO - 2014-03-28T16:53:02.524Z] Session [6cf08380-b699-11e3-b737-ff38291f8e79] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34","webSecurityEnabled":true} -[INFO - 2014-03-28T16:53:02.524Z] Session [6cf08380-b699-11e3-b737-ff38291f8e79] - page.customHeaders: - {} -[INFO - 2014-03-28T16:53:02.524Z] Session [6cf08380-b699-11e3-b737-ff38291f8e79] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"1.9.7","driverName":"ghostdriver","driverVersion":"1.1.0","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}} -[INFO - 2014-03-28T16:53:02.524Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 6cf08380-b699-11e3-b737-ff38291f8e79 -[INFO - 2014-03-28T16:53:02.886Z] GhostDriver - Main - running on port 46548 -[INFO - 2014-03-28T16:53:02.909Z] Session [6d2b69a0-b699-11e3-9a0c-fba94821bfd4] - page.settings - {"XSSAuditingEnabled":false,"javascriptCanCloseWindows":true,"javascriptCanOpenWindows":true,"javascriptEnabled":true,"loadImages":true,"localToRemoteUrlAccessEnabled":false,"userAgent":"Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34","webSecurityEnabled":true} -[INFO - 2014-03-28T16:53:02.909Z] Session [6d2b69a0-b699-11e3-9a0c-fba94821bfd4] - page.customHeaders: - {} -[INFO - 2014-03-28T16:53:02.909Z] Session [6d2b69a0-b699-11e3-9a0c-fba94821bfd4] - Session.negotiatedCapabilities - {"browserName":"phantomjs","version":"1.9.7","driverName":"ghostdriver","driverVersion":"1.1.0","platform":"linux-unknown-64bit","javascriptEnabled":true,"takesScreenshot":true,"handlesAlerts":false,"databaseEnabled":false,"locationContextEnabled":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"cssSelectorsEnabled":true,"webStorageEnabled":false,"rotatable":false,"acceptSslCerts":false,"nativeEvents":true,"proxy":{"proxyType":"direct"}} -[INFO - 2014-03-28T16:53:02.909Z] SessionManagerReqHand - _postNewSessionCommand - New Session Created: 6d2b69a0-b699-11e3-9a0c-fba94821bfd4 diff --git a/proxy.js b/proxy.js index 0341346..ba478f2 100644 --- a/proxy.js +++ b/proxy.js @@ -1,28 +1,30 @@ -// Copyright 2013 Selenium committers +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Defines functions for configuring a webdriver proxy: - *
        
        - * 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();
        - * 
        + * 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(); */ 'use strict'; @@ -30,28 +32,13 @@ var util = require('util'); -/** - * Proxy configuration object, as defined by the WebDriver wire protocol. - * @typedef {( - * {proxyType: string}| - * {proxyType: string, - * proxyAutoconfigUrl: string}| - * {proxyType: string, - * ftpProxy: string, - * httpProxy: string, - * sslProxy: string, - * noProxy: string})} - */ -var ProxyConfig; - - // PUBLIC API /** * Configures WebDriver to bypass all browser proxies. - * @return {!ProxyConfig} A new proxy configuration object. + * @return {!webdriver.ProxyConfig} A new proxy configuration object. */ exports.direct = function() { return {proxyType: 'direct'}; @@ -61,14 +48,13 @@ exports.direct = function() { /** * Manually configures the browser proxy. The following options are * supported: - *
          - *
        • {@code ftp}: Proxy host to use for FTP requests - *
        • {@code http}: Proxy host to use for HTTP requests - *
        • {@code https}: Proxy host to use for HTTPS requests - *
        • {@code bypass}: A list of hosts requests should directly connect to, + * + * - `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. - *
        * * Behavior is undefined for FTP, HTTP, and HTTPS requests if the * corresponding key is omitted from the configuration options. @@ -78,7 +64,7 @@ exports.direct = function() { * https: (string|undefined), * bypass: (string|!Array.|undefined)}} options Proxy * configuration options. - * @return {!ProxyConfig} A new proxy configuration object. + * @return {!webdriver.ProxyConfig} A new proxy configuration object. */ exports.manual = function(options) { return { @@ -96,7 +82,7 @@ exports.manual = function(options) { * Configures WebDriver to configure the browser proxy using the PAC file at * the given URL. * @param {string} url URL for the PAC proxy to use. - * @return {!ProxyConfig} A new proxy configuration object. + * @return {!webdriver.ProxyConfig} A new proxy configuration object. */ exports.pac = function(url) { return { @@ -108,7 +94,7 @@ exports.pac = function(url) { /** * Configures WebDriver to use the current system's proxy. - * @return {!ProxyConfig} A new proxy configuration object. + * @return {!webdriver.ProxyConfig} A new proxy configuration object. */ exports.system = function() { return {proxyType: 'system'}; diff --git a/remote/index.js b/remote/index.js index 8c45f8e..3ac756b 100644 --- a/remote/index.js +++ b/remote/index.js @@ -1,27 +1,34 @@ -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 spawn = require('child_process').spawn, - os = require('os'), +var AdmZip = require('adm-zip'), + AdmConstants = require('adm-zip/util/constants'), + fs = require('fs'), path = require('path'), url = require('url'), util = require('util'); -var promise = require('../').promise, +var _base = require('../_base'), + webdriver = require('../'), + promise = require('../').promise, httpUtil = require('../http/util'), + exec = require('../io/exec'), net = require('../net'), portprober = require('../net/portprober'); @@ -29,23 +36,23 @@ var promise = require('../').promise, /** * Configuration options for a DriverService instance. - *
          - *
        • {@code 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. - *
        • {@code args} - The arguments to pass to the service. If a promise is - * provided, the service will wait for it to resolve before starting. - *
        • {@code path} - The base path on the server for the WebDriver wire - * protocol (e.g. '/wd/hub'). Defaults to '/'. - *
        • {@code env} - The environment variables that should be visible to the - * server process. Defaults to inheriting the current process's - * environment. - *
        • {@code stdio} - IO configuration for the spawned server process. For - * more information, refer to the documentation of - * {@code child_process.spawn}. - *
        + * + * - `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`. * * @typedef {{ + * loopback: (boolean|undefined), * port: (number|!webdriver.promise.Promise.), * args: !(Array.|webdriver.promise.Promise.>), * path: (string|undefined), @@ -59,10 +66,10 @@ var ServiceOptions; /** * 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. + * It is expected that the driver server implements the + * https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol. + * Furthermore, the managed server should support multiple concurrent sessions, + * so that this class may be reused for multiple clients. * * @param {string} executable Path to the executable to run. * @param {!ServiceOptions} options Configuration options for the service. @@ -73,6 +80,9 @@ function DriverService(executable, options) { /** @private {string} */ this.executable_ = executable; + /** @private {boolean} */ + this.loopbackOnly_ = !!options.loopback; + /** @private {(number|!webdriver.promise.Promise.)} */ this.port_ = options.port; @@ -89,6 +99,21 @@ function DriverService(executable, options) { /** @private {(string|!Array.)} */ this.stdio_ = options.stdio || 'ignore'; + + /** + * A promise for the managed subprocess, or null if the server has not been + * started yet. This promise will never be rejected. + * @private {promise.Promise.} + */ + this.command_ = null; + + /** + * Promise that resolves to the server's address or null if the server has + * not been started. This promise will be rejected if the server terminates + * before it starts accepting WebDriver requests. + * @private {promise.Promise.} + */ + this.address_ = null; } @@ -100,26 +125,6 @@ function DriverService(executable, options) { DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000; -/** @private {child_process.ChildProcess} */ -DriverService.prototype.process_ = null; - - -/** - * Promise that resolves to the server's address or null if the server has not - * been started. - * @private {webdriver.promise.Promise.} - */ -DriverService.prototype.address_ = null; - - -/** - * Promise that tracks the status of shutting down the server, or null if the - * server is not currently shutting down. - * @private {webdriver.promise.Promise} - */ -DriverService.prototype.shutdownHook_ = null; - - /** * @return {!webdriver.promise.Promise.} A promise that resolves to * the server's address. @@ -134,6 +139,8 @@ DriverService.prototype.address = function() { /** + * Returns whether the underlying process is still running. This does not take + * into account whether the process is in the process of shutting down. * @return {boolean} Whether the underlying service process is running. */ DriverService.prototype.isRunning = function() { @@ -145,7 +152,7 @@ DriverService.prototype.isRunning = function() { * Starts the server if it is not already running. * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the * server to start accepting requests. Defaults to 30 seconds. - * @return {!webdriver.promise.Promise.} A promise that will resolve + * @return {!promise.Promise.} 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. @@ -158,56 +165,53 @@ DriverService.prototype.start = function(opt_timeoutMs) { var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS; var self = this; + this.command_ = promise.defer(); this.address_ = promise.defer(); this.address_.fulfill(promise.when(this.port_, function(port) { if (port <= 0) { throw Error('Port must be > 0: ' + port); } return promise.when(self.args_, function(args) { - self.process_ = spawn(self.executable_, args, { + var command = exec(self.executable_, { + args: args, env: self.env_, stdio: self.stdio_ - }).once('exit', onServerExit); + }); + + self.command_.fulfill(command); - // This process should not wait on the spawned child, however, we do - // want to ensure the child is killed when this process exits. - self.process_.unref(); - process.once('exit', killServer); + var earlyTermination = command.result().then(function(result) { + var error = result.code == null ? + Error('Server was killed with ' + result.signal) : + Error('Server terminated early with status ' + result.code); + self.address_.reject(error); + self.address_ = null; + self.command_ = null; + throw error; + }); var serverUrl = url.format({ protocol: 'http', - hostname: net.getAddress() || net.getLoopbackAddress(), + hostname: !self.loopbackOnly_ && net.getAddress() || + net.getLoopbackAddress(), port: port, pathname: self.path_ }); - return httpUtil.waitForServer(serverUrl, timeout).then(function() { + return new promise.Promise(function(fulfill, reject) { + var ready = httpUtil.waitForServer(serverUrl, timeout) + .then(fulfill, reject); + earlyTermination.thenCatch(function(e) { + ready.cancel(e); + reject(Error(e.message)); + }); + }).then(function() { return serverUrl; }); }); })); return this.address_; - - function onServerExit(code, signal) { - self.address_.reject(code == null ? - Error('Server was killed with ' + signal) : - Error('Server exited with ' + code)); - - if (self.shutdownHook_) { - self.shutdownHook_.fulfill(); - } - - self.shutdownHook_ = null; - self.address_ = null; - self.process_ = null; - process.removeListener('exit', killServer); - } - - function killServer() { - process.removeListener('exit', killServer); - self.process_ && self.process_.kill('SIGTERM'); - } }; @@ -219,26 +223,12 @@ DriverService.prototype.start = function(opt_timeoutMs) { * the server has been stopped. */ DriverService.prototype.kill = function() { - if (!this.address_) { + if (!this.address_ || !this.command_) { return promise.fulfilled(); // Not currently running. } - - if (!this.shutdownHook_) { - // No process: still starting; wait on address. - // Otherwise, kill the process now. Exit handler will resolve the - // shutdown hook. - if (this.process_) { - this.shutdownHook_ = promise.defer(); - this.process_.kill('SIGTERM'); - } else { - var self = this; - this.shutdownHook_ = this.address_.thenFinally(function() { - self.process_ && self.process_.kill('SIGTERM'); - }); - } - } - - return this.shutdownHook_; + return this.command_.then(function(command) { + command.kill('SIGTERM'); + }); }; @@ -255,18 +245,28 @@ DriverService.prototype.stop = function() { /** - * Manages the life and death of the Selenium standalone server. The server - * may be obtained from http://selenium-release.storage.googleapis.com/index.html. + * Manages the life and death of the + * + * standalone Selenium server. + * * @param {string} jar Path to the Selenium server jar. - * @param {!SeleniumServer.Options} options Configuration options for the + * @param {SeleniumServer.Options=} opt_options Configuration options for the * server. - * @throws {Error} If an invalid port is specified. + * @throws {Error} If the path to the Selenium jar is not specified or if an + * invalid port is specified. * @constructor * @extends {DriverService} */ -function SeleniumServer(jar, options) { - if (options.port < 0) +function SeleniumServer(jar, opt_options) { + if (!jar) { + throw Error('Path to the Selenium jar not specified'); + } + + var options = opt_options || {}; + + if (options.port < 0) { throw Error('Port must be >= 0: ' + options.port); + } var port = options.port || portprober.findFreePort(); var args = promise.when(options.jvmArgs || [], function(jvmArgs) { @@ -278,6 +278,7 @@ function SeleniumServer(jar, options) { }); DriverService.call(this, 'java', { + loopback: options.loopback, port: port, args: args, path: '/wd/hub', @@ -290,23 +291,23 @@ util.inherits(SeleniumServer, DriverService); /** * Options for the Selenium server: - *

          - *
        • {@code 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. - *
        • {@code args} - The arguments to pass to the service. If a promise is - * provided, the service will wait for it to resolve before starting. - *
        • {@code jvmArgs} - The arguments to pass to the JVM. If a promise is - * provided, the service will wait for it to resolve before starting. - *
        • {@code env} - The environment variables that should be visible to the - * server process. Defaults to inheriting the current process's - * environment. - *
        • {@code stdio} - IO configuration for the spawned server process. For - * more information, refer to the documentation of - * {@code child_process.spawn}. - *
        + * + * - `loopback` - Whether the server 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. + * - `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`. * * @typedef {{ + * loopback: (boolean|undefined), * port: (number|!webdriver.promise.Promise.), * args: !(Array.|webdriver.promise.Promise.>), * jvmArgs: (!Array.| @@ -319,7 +320,54 @@ util.inherits(SeleniumServer, DriverService); SeleniumServer.Options; + +/** + * A {@link webdriver.FileDetector} that may be used when running + * against a remote + * [Selenium server](http://selenium-release.storage.googleapis.com/index.html). + * + * When a file path on the local machine running this script is entered with + * {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector + * will transfer the specified file to the Selenium server's host; the sendKeys + * command will be updated to use the transfered file's path. + * + * __Note:__ This class depends on a non-standard command supported on the + * Java Selenium server. The file detector will fail if used with a server that + * only supports standard WebDriver commands (such as the ChromeDriver). + * + * @constructor + * @extends {webdriver.FileDetector} + * @final + */ +var FileDetector = function() {}; +util.inherits(webdriver.FileDetector, FileDetector); + + +/** @override */ +FileDetector.prototype.handleFile = function(driver, filePath) { + return promise.checkedNodeCall(fs.stat, filePath).then(function(stats) { + if (stats.isDirectory()) { + throw TypeError('Uploading directories is not supported: ' + filePath); + } + + var zip = new AdmZip(); + zip.addLocalFile(filePath); + zip.getEntries()[0].header.method = AdmConstants.STORED; + + var command = new webdriver.Command(webdriver.CommandName.UPLOAD_FILE) + .setParameter('file', zip.toBuffer().toString('base64')); + return driver.schedule(command, + 'remote.FileDetector.handleFile(' + filePath + ')'); + }, function(err) { + if (err.code === 'ENOENT') { + return filePath; // Not a file; return original input. + } + throw err; + }); +}; + // PUBLIC API exports.DriverService = DriverService; +exports.FileDetector = FileDetector; exports.SeleniumServer = SeleniumServer; diff --git a/safari.js b/safari.js new file mode 100644 index 0000000..d6780df --- /dev/null +++ b/safari.js @@ -0,0 +1,538 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Defines a WebDriver client for Safari. Before using this + * module, you must install the + * [latest version](http://selenium-release.storage.googleapis.com/index.html) + * of the SafariDriver browser extension; using Safari for normal browsing is + * not recommended once the extension has been installed. You can, and should, + * disable the extension when the browser is not being used with WebDriver. + */ + +'use strict'; + +var events = require('events'); +var fs = require('fs'); +var http = require('http'); +var path = require('path'); +var url = require('url'); +var util = require('util'); +var ws = require('ws'); + +var webdriver = require('./'); +var promise = webdriver.promise; +var _base = require('./_base'); +var io = require('./io'); +var exec = require('./io/exec'); +var portprober = require('./net/portprober'); + + +/** @const */ +var CLIENT_PATH = _base.isDevMode() + ? path.join(__dirname, + '../../../build/javascript/safari-driver/client.js') + : path.join(__dirname, 'lib/safari/client.js'); + + +/** @const */ +var LIBRARY_DIR = process.platform === 'darwin' + ? path.join('/Users', process.env['USER'], 'Library/Safari') + : path.join(process.env['APPDATA'], 'Apple Computer', 'Safari'); + + +/** @const */ +var SESSION_DATA_FILES = (function() { + if (process.platform === 'darwin') { + var libraryDir = path.join('/Users', process.env['USER'], 'Library'); + return [ + path.join(libraryDir, 'Caches/com.apple.Safari/Cache.db'), + path.join(libraryDir, 'Cookies/Cookies.binarycookies'), + path.join(libraryDir, 'Cookies/Cookies.plist'), + path.join(libraryDir, 'Safari/History.plist'), + path.join(libraryDir, 'Safari/LastSession.plist'), + path.join(libraryDir, 'Safari/LocalStorage'), + path.join(libraryDir, 'Safari/Databases') + ]; + } else if (process.platform === 'win32') { + var appDataDir = path.join(process.env['APPDATA'], + 'Apple Computer', 'Safari'); + var localDataDir = path.join(process.env['LOCALAPPDATA'], + 'Apple Computer', 'Safari'); + return [ + path.join(appDataDir, 'History.plist'), + path.join(appDataDir, 'LastSession.plist'), + path.join(appDataDir, 'Cookies/Cookies.plist'), + path.join(appDataDir, 'Cookies/Cookies.binarycookies'), + path.join(localDataDir, 'Cache.db'), + path.join(localDataDir, 'Databases'), + path.join(localDataDir, 'LocalStorage') + ]; + } else { + return []; + } +})(); + + +/** @typedef {{port: number, address: string, family: string}} */ +var Host; + + +/** + * A basic HTTP/WebSocket server used to communicate with the SafariDriver + * browser extension. + * @constructor + * @extends {events.EventEmitter} + */ +var Server = function() { + events.EventEmitter.call(this); + + var server = http.createServer(function(req, res) { + if (req.url === '/favicon.ico') { + res.writeHead(204); + res.end(); + return; + } + + var query = url.parse(req.url).query || ''; + if (query.indexOf('url=') == -1) { + var address = server.address() + var host = address.address + ':' + address.port; + res.writeHead(302, {'Location': 'http://' + host + '?url=ws://' + host}); + res.end(); + } + + fs.readFile(CLIENT_PATH, 'utf8', function(err, data) { + if (err) { + res.writeHead(500, {'Content-Type': 'text/plain'}); + res.end(err.stack); + return; + } + var content = ''; + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8', + 'Content-Length': Buffer.byteLength(content, 'utf8'), + }); + res.end(content); + }); + }); + + var wss = new ws.Server({server: server}); + wss.on('connection', this.emit.bind(this, 'connection')); + + /** + * Starts the server on a random port. + * @return {!webdriver.promise.Promise} A promise that will resolve + * with the server host when it has fully started. + */ + this.start = function() { + if (server.address()) { + return promise.fulfilled(server.address()); + } + return portprober.findFreePort('localhost').then(function(port) { + return promise.checkedNodeCall( + server.listen.bind(server, port, 'localhost')); + }).then(function() { + return server.address(); + }); + }; + + /** + * Stops the server. + * @return {!webdriver.promise.Promise} A promise that will resolve when the + * server has closed all connections. + */ + this.stop = function() { + return new promise.Promise(function(fulfill) { + server.close(fulfill); + }); + }; + + /** + * @return {Host} This server's host info. + * @throws {Error} If the server is not running. + */ + this.address = function() { + var addr = server.address(); + if (!addr) { + throw Error('There server is not running!'); + } + return addr; + }; +}; +util.inherits(Server, events.EventEmitter); + + +/** + * @return {!promise.Promise} A promise that will resolve with the path + * to Safari on the current system. + */ +function findSafariExecutable() { + switch (process.platform) { + case 'darwin': + return promise.fulfilled( + '/Applications/Safari.app/Contents/MacOS/Safari'); + + case 'win32': + var files = [ + process.env['PROGRAMFILES'] || '\\Program Files', + process.env['PROGRAMFILES(X86)'] || '\\Program Files (x86)' + ].map(function(prefix) { + return path.join(prefix, 'Safari\\Safari.exe'); + }); + return io.exists(files[0]).then(function(exists) { + return exists ? files[0] : io.exists(files[1]).then(function(exists) { + if (exists) { + return files[1]; + } + throw Error('Unable to find Safari on the current system'); + }); + }); + + default: + return promise.rejected( + Error('Safari is not supported on the current platform: ' + + process.platform)); + } +} + + +/** + * @param {string} url The URL to connect to. + * @return {!promise.Promise} A promise for the path to a file that + * Safari can open on start-up to trigger a new connection to the WebSocket + * server. + */ +function createConnectFile(url) { + return io.tmpFile({postfix: '.html'}).then(function(f) { + var writeFile = promise.checkedNodeCall(fs.writeFile, + f, + '', + {encoding: 'utf8'}); + return writeFile.then(function() { + return f; + }); + }); +} + + +/** + * Deletes all session data files if so desired. + * @param {!Object} desiredCapabilities . + * @return {!Array} A list of promises for the deleted files. + */ +function cleanSession(desiredCapabilities) { + if (!desiredCapabilities) { + return []; + } + var options = desiredCapabilities[OPTIONS_CAPABILITY_KEY]; + if (!options) { + return []; + } + if (!options['cleanSession']) { + return []; + } + return SESSION_DATA_FILES.map(function(file) { + return io.unlink(file); + }); +} + + +/** + * @constructor + * @implements {webdriver.CommandExecutor} + */ +var CommandExecutor = function() { + /** @private {Server} */ + this.server_ = null; + + /** @private {ws.WebSocket} */ + this.socket_ = null; + + /** @private {promise.Promise.} */ + this.safari_ = null; +}; + + +/** @override */ +CommandExecutor.prototype.execute = function(command, callback) { + var safariCommand = JSON.stringify({ + 'origin': 'webdriver', + 'type': 'command', + 'command': { + 'id': _base.require('goog.string').getRandomString(), + 'name': command.getName(), + 'parameters': command.getParameters() + } + }); + var self = this; + + switch (command.getName()) { + case webdriver.CommandName.NEW_SESSION: + this.startSafari_(command).then(sendCommand, callback); + break; + + case webdriver.CommandName.QUIT: + this.destroySession_().then(function() { + callback(null, _base.require('bot.response').createResponse(null)); + }, callback); + break; + + default: + sendCommand(); + break; + } + + function sendCommand() { + new promise.Promise(function(fulfill, reject) { + // TODO: support reconnecting with the extension. + if (!self.socket_) { + self.destroySession_().thenFinally(function() { + reject(Error('The connection to the SafariDriver was closed')); + }); + return; + } + + self.socket_.send(safariCommand, function(err) { + if (err) { + reject(err); + return; + } + }); + + self.socket_.once('message', function(data) { + try { + data = JSON.parse(data); + } catch (ex) { + reject(Error('Failed to parse driver message: ' + data)); + return; + } + fulfill(data['response']); + }); + + }).then(function(value) { + callback(null, value); + }, callback); + } +}; + + +/** + * @param {!webdriver.Command} command . + * @private + */ +CommandExecutor.prototype.startSafari_ = function(command) { + this.server_ = new Server(); + + this.safari_ = this.server_.start().then(function(address) { + var tasks = cleanSession(command.getParameters()['desiredCapabilities']); + tasks.push( + findSafariExecutable(), + createConnectFile( + 'http://' + address.address + ':' + address.port)); + return promise.all(tasks).then(function(tasks) { + var exe = tasks[tasks.length - 2]; + var html = tasks[tasks.length - 1]; + return exec(exe, {args: [html]}); + }); + }); + + var connected = promise.defer(); + var self = this; + var start = Date.now(); + var timer = setTimeout(function() { + connected.reject(Error( + 'Failed to connect to the SafariDriver after ' + (Date.now() - start) + + ' ms; Have you installed the latest extension from ' + + 'http://selenium-release.storage.googleapis.com/index.html?')); + }, 10 * 1000); + this.server_.once('connection', function(socket) { + clearTimeout(timer); + self.socket_ = socket; + socket.once('close', function() { + self.socket_ = null; + }); + connected.fulfill(); + }); + return connected.promise; +}; + + +/** + * Destroys the active session by stopping the WebSocket server and killing the + * Safari subprocess. + * @private + */ +CommandExecutor.prototype.destroySession_ = function() { + var tasks = []; + if (this.server_) { + tasks.push(this.server_.stop()); + } + if (this.safari_) { + tasks.push(this.safari_.then(function(safari) { + safari.kill(); + return safari.result(); + })); + } + var self = this; + return promise.all(tasks).thenFinally(function() { + self.server_ = null; + self.socket_ = null; + self.safari_ = null; + }); +}; + + +/** @const */ +var OPTIONS_CAPABILITY_KEY = 'safari.options'; + + + +/** + * Configuration options specific to the {@link Driver SafariDriver}. + * @constructor + * @extends {webdriver.Serializable} + */ +var Options = function() { + webdriver.Serializable.call(this); + + /** @private {Object} */ + this.options_ = null; + + /** @private {webdriver.logging.Preferences} */ + this.logPrefs_ = null; +}; +util.inherits(Options, webdriver.Serializable); + + +/** + * Extracts the SafariDriver specific options from the given capabilities + * object. + * @param {!webdriver.Capabilities} capabilities The capabilities object. + * @return {!Options} The ChromeDriver options. + */ +Options.fromCapabilities = function(capabilities) { + var options = new Options(); + + var o = capabilities.get(OPTIONS_CAPABILITY_KEY); + if (o instanceof Options) { + options = o; + } else if (o) { + options.setCleanSession(o.cleanSession); + } + + if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) { + options.setLoggingPrefs( + capabilities.get(webdriver.Capability.LOGGING_PREFS)); + } + + return options; +}; + + +/** + * Sets whether to force Safari to start with a clean session. Enabling this + * option will cause all global browser data to be deleted. + * @param {boolean} clean Whether to make sure the session has no cookies, + * cache entries, local storage, or databases. + * @return {!Options} A self reference. + */ +Options.prototype.setCleanSession = function(clean) { + if (!this.options_) { + this.options_ = {}; + } + this.options_['cleanSession'] = clean; + return this; +}; + + +/** + * Sets the logging preferences for the new session. + * @param {!webdriver.logging.Preferences} prefs The logging preferences. + * @return {!Options} A self reference. + */ +Options.prototype.setLoggingPrefs = function(prefs) { + this.logPrefs_ = prefs; + return this; +}; + + +/** + * Converts this options instance to a {@link webdriver.Capabilities} object. + * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge + * these options into, if any. + * @return {!webdriver.Capabilities} The capabilities. + */ +Options.prototype.toCapabilities = function(opt_capabilities) { + var capabilities = opt_capabilities || webdriver.Capabilities.safari(); + if (this.logPrefs_) { + capabilities.set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_); + } + if (this.options_) { + capabilities.set(OPTIONS_CAPABILITY_KEY, this); + } + return capabilities; +}; + + +/** + * Converts this instance to its JSON wire protocol representation. Note this + * function is an implementation detail not intended for general use. + * @return {!Object} The JSON wire protocol representation of this + * instance. + * @override + */ +Options.prototype.serialize = function() { + return this.options_ || {}; +}; + + + +/** + * A WebDriver client for Safari. This class should never be instantiated + * directly; instead, use the {@link selenium-webdriver.Builder}: + * + * var driver = new Builder() + * .forBrowser('safari') + * .build(); + * + * @param {(Options|webdriver.Capabilities)=} opt_config The configuration + * options for the new session. + * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to create + * the driver under. + * @constructor + * @extends {webdriver.WebDriver} + */ +var Driver = function(opt_config, opt_flow) { + var executor = new CommandExecutor(); + var capabilities = + opt_config instanceof Options ? opt_config.toCapabilities() : + (opt_config || webdriver.Capabilities.safari()); + + var driver = webdriver.WebDriver.createSession( + executor, capabilities, opt_flow); + webdriver.WebDriver.call( + this, driver.getSession(), executor, driver.controlFlow()); +}; +util.inherits(Driver, webdriver.WebDriver); + + +// Public API + + +exports.Driver = Driver; +exports.Options = Options; diff --git a/test/_base_test.js b/test/_base_test.js new file mode 100644 index 0000000..8765153 --- /dev/null +++ b/test/_base_test.js @@ -0,0 +1,129 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 assert = require('assert'), + fs = require('fs'), + path = require('path'); + +var base = require('../_base'); + +describe('Context', function() { + it('does not pollute the global scope', function() { + assert.equal('undefined', typeof goog); + + var context = new base.Context(); + assert.equal('undefined', typeof goog); + assert.equal('object', typeof context.closure.goog); + + context.closure.goog.require('goog.array'); + assert.equal('undefined', typeof goog); + assert.equal('object', typeof context.closure.goog.array); + }); +}); + + +function haveGenerators() { + try { + // Test for generator support. + new Function('function* x() {}'); + return true; + } catch (ex) { + return false; + } +} + + +function runClosureTest(file) { + var name = path.basename(file); + name = name.substring(0, name.length - '.js'.length); + + // Generator tests will fail to parse in ES5, so mark those tests as + // pending under ES5. + if (name.indexOf('_generator_') != -1 && !haveGenerators()) { + it(name); + return; + } + + describe(name, function() { + var context = new base.Context(true); + context.closure.document.title = name; + if (process.env.VERBOSE == '1') { + context.closure.goog.require('webdriver.logging'); + context.closure.goog.module.get('webdriver.logging') + .installConsoleHandler(); + } else { + // Null out console so everything loads silently. + context.closure.console = null; + } + context.closure.CLOSURE_IMPORT_SCRIPT(file); + + var tc = context.closure.G_testRunner.testCase; + if (!tc) { + tc = new context.closure.goog.testing.TestCase(name); + tc.autoDiscoverTests(); + } + + var shouldRunTests = tc.shouldRunTests(); + var allTests = tc.getTests(); + allTests.forEach(function(test) { + if (!shouldRunTests) { + it(test.name); + return; + } + + it(test.name, function(done) { + tc.setTests([test]); + tc.setCompletedCallback(function() { + if (tc.isSuccess()) { + return done(); + } + var results = tc.getTestResults(); + done(Error('\n' + Object.keys(results).map(function(name) { + var msg = + [name + ': ' + (results[name].length ? 'FAILED' : 'PASSED')]; + if (results[name].length) { + msg = msg.concat(results[name]); + } + return msg.join('\n'); + }).join('\n'))); + }); + tc.runTests(); + }); + }); + }); +} + + +function findTests(dir) { + fs.readdirSync(dir).forEach(function(name) { + var file = path.join(dir, name); + + var stat = fs.statSync(file); + if (stat.isDirectory() && name !== 'atoms' && name !== 'e2e') { + findTests(file); + return; + } + + var l = file.length - '_test.js'.length; + if (l >= 0 && file.indexOf('_test.js', l) == l) { + runClosureTest(file); + } + }); +} + +findTests(path.join( + __dirname, base.isDevMode() ? '../../..' : '../lib', 'webdriver/test')); diff --git a/test/actions_test.js b/test/actions_test.js new file mode 100644 index 0000000..8d6794a --- /dev/null +++ b/test/actions_test.js @@ -0,0 +1,53 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Browser = require('..').Browser, + By = require('..').By, + until = require('..').until, + test = require('../lib/test'), + fileServer = require('../lib/test/fileserver'); + + +test.suite(function(env) { + var driver; + test.beforeEach(function() { driver = env.builder().build(); }); + test.afterEach(function() { driver.quit(); }); + + test.ignore(env.browsers(Browser.PHANTOM_JS, Browser.SAFARI)). + describe('WebDriver.actions()', function() { + + test.it('can move to and click element in an iframe', function() { + driver.get(fileServer.whereIs('click_tests/click_in_iframe.html')); + + driver.wait(until.elementLocated(By.id('ifr')), 5000) + .then(function(frame) { + driver.switchTo().frame(frame); + }); + + var link = driver.findElement(By.id('link')); + driver.actions() + .mouseMove(link) + .click() + .perform(); + + driver.wait(until.titleIs('Submitted Successfully!'), 5000); + }); + + }); +}); diff --git a/test/chrome/options_test.js b/test/chrome/options_test.js index 2398ddf..980f2a9 100644 --- a/test/chrome/options_test.js +++ b/test/chrome/options_test.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -44,47 +46,51 @@ describe('chrome.Options', function() { }); it('should rebuild options from wire representation', function() { + var expectedExtension = fs.readFileSync(__filename, 'base64'); var caps = webdriver.Capabilities.chrome().set('chromeOptions', { args: ['a', 'b'], - extensions: [1, 2], + extensions: [__filename], binary: 'binaryPath', - logFile: 'logFilePath', + logPath: 'logFilePath', detach: true, localState: 'localStateValue', prefs: 'prefsValue' }); var options = chrome.Options.fromCapabilities(caps); - - assert(options.args_.length).equalTo(2); - assert(options.args_[0]).equalTo('a'); - assert(options.args_[1]).equalTo('b'); - assert(options.extensions_.length).equalTo(2); - assert(options.extensions_[0]).equalTo(1); - assert(options.extensions_[1]).equalTo(2); - assert(options.binary_).equalTo('binaryPath'); - assert(options.logFile_).equalTo('logFilePath'); - assert(options.detach_).equalTo(true); - assert(options.localState_).equalTo('localStateValue'); - assert(options.prefs_).equalTo('prefsValue'); + var json = options.serialize(); + + assert(json.args.length).equalTo(2); + assert(json.args[0]).equalTo('a'); + assert(json.args[1]).equalTo('b'); + assert(json.extensions.length).equalTo(1); + assert(json.extensions[0]).equalTo(expectedExtension); + assert(json.binary).equalTo('binaryPath'); + assert(json.logPath).equalTo('logFilePath'); + assert(json.detach).equalTo(true); + assert(json.localState).equalTo('localStateValue'); + assert(json.prefs).equalTo('prefsValue'); }); it('should rebuild options from incomplete wire representation', function() { var caps = webdriver.Capabilities.chrome().set('chromeOptions', { - logFile: 'logFilePath' + logPath: 'logFilePath' }); var options = chrome.Options.fromCapabilities(caps); - var json = options.toJSON(); - - assert(json.args.length).equalTo(0); + var json = options.serialize(); + assert(json.args).isUndefined(); assert(json.binary).isUndefined(); - assert(json.detach).isFalse(); - assert(json.extensions.length).equalTo(0); + assert(json.detach).isUndefined(); + assert(json.excludeSwitches).isUndefined(); + assert(json.extensions).isUndefined(); assert(json.localState).isUndefined(); - assert(json.logFile).equalTo('logFilePath'); + assert(json.logPath).equalTo('logFilePath'); assert(json.prefs).isUndefined(); + assert(json.minidumpPath).isUndefined(); + assert(json.mobileEmulation).isUndefined(); + assert(json.perfLoggingPrefs).isUndefined(); }); it('should extract supported WebDriver capabilities', function() { @@ -103,26 +109,28 @@ describe('chrome.Options', function() { describe('addArguments', function() { it('takes var_args', function() { var options = new chrome.Options(); - assert(options.args_.length).equalTo(0); + assert(options.serialize().args).isUndefined(); options.addArguments('a', 'b'); - assert(options.args_.length).equalTo(2); - assert(options.args_[0]).equalTo('a'); - assert(options.args_[1]).equalTo('b'); + var json = options.serialize(); + assert(json.args.length).equalTo(2); + assert(json.args[0]).equalTo('a'); + assert(json.args[1]).equalTo('b'); }); it('flattens input arrays', function() { var options = new chrome.Options(); - assert(options.args_.length).equalTo(0); + assert(options.serialize().args).isUndefined(); options.addArguments(['a', 'b'], 'c', [1, 2], 3); - assert(options.args_.length).equalTo(6); - assert(options.args_[0]).equalTo('a'); - assert(options.args_[1]).equalTo('b'); - assert(options.args_[2]).equalTo('c'); - assert(options.args_[3]).equalTo(1); - assert(options.args_[4]).equalTo(2); - assert(options.args_[5]).equalTo(3); + var json = options.serialize(); + assert(json.args.length).equalTo(6); + assert(json.args[0]).equalTo('a'); + assert(json.args[1]).equalTo('b'); + assert(json.args[2]).equalTo('c'); + assert(json.args[3]).equalTo(1); + assert(json.args[4]).equalTo(2); + assert(json.args[5]).equalTo(3); }); }); @@ -152,10 +160,10 @@ describe('chrome.Options', function() { }); }); - describe('toJSON', function() { + describe('serialize', function() { it('base64 encodes extensions', function() { var expected = fs.readFileSync(__filename, 'base64'); - var wire = new chrome.Options().addExtensions(__filename).toJSON(); + var wire = new chrome.Options().addExtensions(__filename).serialize(); assert(wire.extensions.length).equalTo(1); assert(wire.extensions[0]).equalTo(expected); }); @@ -181,7 +189,7 @@ describe('chrome.Options', function() { var proxyPrefs = {}; var loggingPrefs = {}; var options = new chrome.Options(). - setLoggingPreferences(loggingPrefs). + setLoggingPrefs(loggingPrefs). setProxy(proxyPrefs); var caps = options.toCapabilities(); @@ -192,14 +200,18 @@ describe('chrome.Options', function() { }); test.suite(function(env) { - env.autoCreateDriver = false; + var driver; + + test.afterEach(function() { + driver.quit(); + }); - describe('options', function() { + describe('Chrome options', function() { test.it('can start Chrome with custom args', function() { var options = new chrome.Options(). addArguments('user-agent=foo;bar'); - var driver = env.builder(). + driver = env.builder(). setChromeOptions(options). build(); diff --git a/test/chrome/service_test.js b/test/chrome/service_test.js new file mode 100644 index 0000000..0e0209e --- /dev/null +++ b/test/chrome/service_test.js @@ -0,0 +1,45 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 webdriver = require('../..'), + chrome = require('../../chrome'), + assert = require('../../testing/assert'); + +var test = require('../../lib/test'); + + +test.suite(function(env) { + describe('chromedriver', function() { + var service; + test.afterEach(function() { + if (service) { + return service.kill(); + } + }); + + test.it('can be started on a custom path', function() { + service = new chrome.ServiceBuilder() + .setUrlBasePath('/foo/bar/baz') + .build(); + return service.start().then(function(url) { + assert(url).endsWith('/foo/bar/baz'); + }); + }); + }); +}, {browsers: ['chrome']}); \ No newline at end of file diff --git a/test/cookie_test.js b/test/cookie_test.js index 700e424..fe4c839 100644 --- a/test/cookie_test.js +++ b/test/cookie_test.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -20,13 +22,20 @@ var assert = require('assert'), var test = require('../lib/test'), fileserver = require('../lib/test/fileserver'), - Browser = test.Browser, + Browser = require('..').Browser, Pages = test.Pages; test.suite(function(env) { var driver; - beforeEach(function() { driver = env.driver; }); + + test.before(function() { + driver = env.builder().build(); + }); + + test.after(function() { + driver.quit(); + }); test.ignore(env.browsers(Browser.SAFARI)). // Cookie handling is broken. describe('Cookie Management;', function() { @@ -56,7 +65,7 @@ test.suite(function(env) { assertHasCookies(cookie1, cookie2); }); - test.ignore(env.browsers(Browser.OPERA)). + test.ignore(env.browsers(Browser.IE)). it('only returns cookies visible to the current page', function() { var cookie1 = createCookieSpec(); var cookie2 = createCookieSpec(); @@ -141,8 +150,7 @@ test.suite(function(env) { assertHasCookies(); }); - test.ignore(env.browsers( - Browser.ANDROID, Browser.FIREFOX, Browser.IE, Browser.OPERA)). + test.ignore(env.browsers(Browser.ANDROID, Browser.FIREFOX, Browser.IE)). it('should retain cookie expiry', function() { var cookie = createCookieSpec(); var expirationDelay = 5 * 1000; diff --git a/test/element_finding_test.js b/test/element_finding_test.js index d1e95f9..02fc8c8 100644 --- a/test/element_finding_test.js +++ b/test/element_finding_test.js @@ -1,36 +1,45 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 fail = require('assert').fail; -var By = require('..').By, +var Browser = require('..').Browser, + By = require('..').By, error = require('..').error, + until = require('..').until, test = require('../lib/test'), assert = require('../testing/assert'), - Browser = test.Browser, Pages = test.Pages; test.suite(function(env) { - var browsers = env.browsers, - waitForTitleToBe = env.waitForTitleToBe; + var browsers = env.browsers; var driver; - beforeEach(function() { driver = env.driver; }); + + test.before(function() { + driver = env.builder().build(); + }); + + test.after(function() { + driver.quit(); + }); describe('finding elements', function() { @@ -40,14 +49,14 @@ test.suite(function(env) { driver.get(Pages.formPage); driver.get(Pages.xhtmlTestPage); driver.findElement(By.linkText('click me')).click(); - waitForTitleToBe('We Arrive Here'); + driver.wait(until.titleIs('We Arrive Here'), 5000); }); describe('By.id()', function() { test.it('should work', function() { driver.get(Pages.xhtmlTestPage); driver.findElement(By.id('linkId')).click(); - waitForTitleToBe('We Arrive Here'); + driver.wait(until.titleIs('We Arrive Here'), 5000); }); test.it('should fail if ID not present on page', function() { @@ -58,9 +67,9 @@ test.suite(function(env) { }); }); - test.ignore(browsers(Browser.ANDROID)).it( - 'should find multiple elements by ID even though that ' + - 'is malformed HTML', + test.it( + 'should find multiple elements by ID even though that is ' + + 'malformed HTML', function() { driver.get(Pages.nestedPage); driver.findElements(By.id('2')).then(function(elements) { @@ -73,14 +82,14 @@ test.suite(function(env) { test.it('should be able to click on link identified by text', function() { driver.get(Pages.xhtmlTestPage); driver.findElement(By.linkText('click me')).click(); - waitForTitleToBe('We Arrive Here'); + driver.wait(until.titleIs('We Arrive Here'), 5000); }); test.it( 'should be able to find elements by partial link text', function() { driver.get(Pages.xhtmlTestPage); driver.findElement(By.partialLinkText('ick me')).click(); - waitForTitleToBe('We Arrive Here'); + driver.wait(until.titleIs('We Arrive Here'), 5000); }); test.it('should work when link text contains equals sign', function() { @@ -143,8 +152,7 @@ test.suite(function(env) { }); }); - test.ignore(browsers(Browser.OPERA)). - it('works on XHTML pages', function() { + test.it('works on XHTML pages', function() { driver.get(test.whereIs('actualXhtmlPage.xhtml')); var el = driver.findElement(By.linkText('Foo')); diff --git a/test/execute_script_test.js b/test/execute_script_test.js new file mode 100644 index 0000000..caf8ddc --- /dev/null +++ b/test/execute_script_test.js @@ -0,0 +1,322 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 path = require('path'); + +var webdriver = require('..'), + Browser = webdriver.Browser, + By = webdriver.By, + assert = require('../testing/assert'), + test = require('../lib/test'); + + +test.suite(function(env) { + var driver; + + test.before(function() { + driver = env.builder().build(); + }); + + test.after(function() { + driver.quit(); + }); + + test.beforeEach(function() { + driver.get(test.Pages.echoPage); + }); + + describe('executeScript;', function() { + var shouldHaveFailed = new Error('Should have failed'); + + test.it('fails if script throws', function() { + execute('throw new Error("boom")') + .then(function() { throw shoudlHaveFailed; }) + .thenCatch(function(e) { + // The java WebDriver server adds a bunch of crap to error messages. + // Error message will just be "JavaScript error" for IE. + assert(e.message).matches(/.*(JavaScript error|boom).*/); + }); + }); + + test.it('fails if script does not parse', function() { + execute('throw function\\*') + .then(function() { throw shoudlHaveFailed; }) + .thenCatch(function(e) { + assert(e).not.equalTo(shouldHaveFailed); + }); + }); + + describe('scripts;', function() { + test.it('do not pollute the global scope', function() { + execute('var x = 1;'); + assert(execute('return typeof x;')).equalTo('undefined'); + }); + + test.it('can set global variables', function() { + execute('window.x = 1234;'); + assert(execute('return x;')).equalTo(1234); + }); + + test.it('may be defined as a function expression', function() { + assert(execute(function() { + return 1234 + 'abc'; + })).equalTo('1234abc'); + }); + }); + + describe('return values;', function() { + + test.it('returns undefined as null', function() { + assert(execute('var x; return x;')).isNull(); + }); + + test.it('can return null', function() { + assert(execute('return null;')).isNull(); + }); + + test.it('can return numbers', function() { + assert(execute('return 1234')).equalTo(1234); + assert(execute('return 3.1456')).equalTo(3.1456); + }); + + test.it('can return strings', function() { + assert(execute('return "hello"')).equalTo('hello'); + }); + + test.it('can return booleans', function() { + assert(execute('return true')).equalTo(true); + assert(execute('return false')).equalTo(false); + }); + + test.it('can return an array of primitives', function() { + execute('var x; return [1, false, null, 3.14, x]') + .then(verifyJson([1, false, null, 3.14, null])); + }); + + test.it('can return nested arrays', function() { + execute('return [[1, 2, [3]]]') + .then(verifyJson([[1, 2, [3]]])); + }); + + test.ignore(env.browsers(Browser.IE, Browser.SAFARI)). + it('can return empty object literal', function() { + execute('return {}').then(verifyJson({})); + }); + + test.it('can return object literals', function() { + execute('return {a: 1, b: false, c: null}').then(function(result) { + verifyJson(['a', 'b', 'c'])(Object.keys(result).sort()); + assert(result.a).equalTo(1); + assert(result.b).equalTo(false); + assert(result.c).isNull(); + }); + }); + + test.it('can return complex object literals', function() { + execute('return {a:{b: "hello"}}').then(verifyJson({a:{b: 'hello'}})); + }); + + test.it('can return dom elements as web elements', function() { + execute('return document.querySelector(".header.host")') + .then(function(result) { + assert(result).instanceOf(webdriver.WebElement); + assert(result.getText()).startsWith('host: '); + }); + }); + + test.it('can return array of dom elements', function() { + execute('var nodes = document.querySelectorAll(".request,.host");' + + 'return [nodes[0], nodes[1]];') + .then(function(result) { + assert(result.length).equalTo(2); + + assert(result[0]).instanceOf(webdriver.WebElement); + assert(result[0].getText()).startsWith('GET '); + + assert(result[1]).instanceOf(webdriver.WebElement); + assert(result[1].getText()).startsWith('host: '); + }); + }); + + test.it('can return a NodeList as an array of web elements', function() { + execute('return document.querySelectorAll(".request,.host");') + .then(function(result) { + assert(result.length).equalTo(2); + + assert(result[0]).instanceOf(webdriver.WebElement); + assert(result[0].getText()).startsWith('GET '); + + assert(result[1]).instanceOf(webdriver.WebElement); + assert(result[1].getText()).startsWith('host: '); + }); + }); + + test.it('can return object literal with element property', function() { + execute('return {a: document.body}').then(function(result) { + assert(result.a).instanceOf(webdriver.WebElement); + assert(result.a.getTagName()).equalTo('body'); + }); + }); + }); + + describe('parameters;', function() { + test.it('can pass numeric arguments', function() { + assert(execute('return arguments[0]', 12)).equalTo(12); + assert(execute('return arguments[0]', 3.14)).equalTo(3.14); + }); + + test.it('can pass boolean arguments', function() { + assert(execute('return arguments[0]', true)).equalTo(true); + assert(execute('return arguments[0]', false)).equalTo(false); + }); + + test.it('can pass string arguments', function() { + assert(execute('return arguments[0]', 'hi')).equalTo('hi'); + }); + + test.it('can pass null arguments', function() { + assert(execute('return arguments[0] === null', null)).equalTo(true); + assert(execute('return arguments[0]', null)).equalTo(null); + }); + + test.it('passes undefined as a null argument', function() { + var x; + assert(execute('return arguments[0] === null', x)).equalTo(true); + assert(execute('return arguments[0]', x)).equalTo(null); + }); + + test.it('can pass multiple arguments', function() { + assert(execute('return arguments.length')).equalTo(0); + assert(execute('return arguments.length', 1, 'a', false)).equalTo(3); + }); + + test.it('can return arguments object as array', function() { + execute('return arguments', 1, 'a', false).then(function(val) { + assert(val.length).equalTo(3); + assert(val[0]).equalTo(1); + assert(val[1]).equalTo('a'); + assert(val[2]).equalTo(false); + }); + }); + + test.it('can pass object literal', function() { + execute( + 'return [typeof arguments[0], arguments[0].a]', {a: 'hello'}) + .then(function(result) { + assert(result[0]).equalTo('object'); + assert(result[1]).equalTo('hello'); + }); + }); + + test.it('WebElement arguments are passed as DOM elements', function() { + var el = driver.findElement(By.tagName('div')); + assert(execute('return arguments[0].tagName.toLowerCase();', el)) + .equalTo('div'); + }); + + test.it('can pass array containing object literals', function() { + execute('return arguments[0]', [{color: "red"}]).then(function(result) { + assert(result.length).equalTo(1); + assert(result[0].color).equalTo('red'); + }); + }); + + test.it('does not modify object literal parameters', function() { + var input = {color: 'red'}; + execute('return arguments[0];', input).then(verifyJson(input)); + }); + }); + + // See https://code.google.com/p/selenium/issues/detail?id=8223. + describe('issue 8223;', function() { + describe('using for..in loops;', function() { + test.it('can return array built from for-loop index', function() { + execute(function() { + var ret = []; + for (var i = 0; i < 3; i++) { + ret.push(i); + } + return ret; + }).then(verifyJson[0, 1, 2]); + }); + + test.it('can copy input array contents', function() { + execute(function(input) { + var ret = []; + for (var i in input) { + ret.push(input[i]); + } + return ret; + }, ['fa', 'fe', 'fi']).then(verifyJson(['fa', 'fe', 'fi'])); + }); + + test.it('can iterate over input object keys', function() { + execute(function(thing) { + var ret = []; + for (var w in thing.words) { + ret.push(thing.words[w].word); + } + return ret; + }, {words: [{word: 'fa'}, {word: 'fe'}, {word: 'fi'}]}) + .then(verifyJson(['fa', 'fe', 'fi'])); + }); + + describe('recursive functions;', function() { + test.it('can build array from input', function() { + var input = ['fa', 'fe', 'fi']; + execute(function(thearray) { + var ret = []; + function build_response(thearray, ret) { + ret.push(thearray.shift()); + return (!thearray.length && ret + || build_response(thearray, ret)); + } + return build_response(thearray, ret); + }, input).then(verifyJson(input)); + }); + + test.it('can build array from elements in object', function() { + var input = {words: [{word: 'fa'}, {word: 'fe'}, {word: 'fi'}]}; + execute(function(thing) { + var ret = []; + function build_response(thing, ret) { + var item = thing.words.shift(); + ret.push(item.word); + return (!thing.words.length && ret + || build_response(thing, ret)); + } + return build_response(thing, ret); + }, input).then(verifyJson(['fa', 'fe', 'fi'])); + }); + }); + }); + }); + + }); + + function verifyJson(expected) { + return function(actual) { + assert(JSON.stringify(actual)).equalTo(JSON.stringify(expected)); + }; + } + + function execute() { + return driver.executeScript.apply(driver, arguments); + } +}); diff --git a/test/fingerprint_test.js b/test/fingerprint_test.js new file mode 100644 index 0000000..5a64494 --- /dev/null +++ b/test/fingerprint_test.js @@ -0,0 +1,57 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 assert = require('../testing/assert'), + test = require('../lib/test'), + Pages = test.Pages; + + +test.suite(function(env) { + var browsers = env.browsers; + + var driver; + test.before(function() { + driver = env.builder().build(); + }); + + test.after(function() { + driver.quit(); + }); + + describe('fingerprinting', function() { + test.it('it should fingerprint the navigator object', function() { + driver.get(Pages.simpleTestPage); + assert(driver.executeScript('return navigator.webdriver')).equalTo(true); + }); + + test.it('fingerprint must not be writable', function() { + driver.get(Pages.simpleTestPage); + assert(driver.executeScript( + 'navigator.webdriver = "ohai"; return navigator.webdriver')) + .equalTo(true); + }); + + test.it('leaves fingerprint on svg pages', function() { + driver.get(Pages.svgPage); + assert(driver.executeScript('return navigator.webdriver')).equalTo(true); + }); + }); + +// Currently only implemented in firefox. +}, {browsers: ['firefox']}); diff --git a/test/firefox/extension_test.js b/test/firefox/extension_test.js new file mode 100644 index 0000000..50936f7 --- /dev/null +++ b/test/firefox/extension_test.js @@ -0,0 +1,96 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 AdmZip = require('adm-zip'), + assert = require('assert'), + crypto = require('crypto'), + fs = require('fs'), + path = require('path'); + +var extension = require('../../firefox/extension'), + io = require('../../io'), + it = require('../../testing').it; + + +var JETPACK_EXTENSION = path.join(__dirname, + '../../lib/test/data/firefox/jetpack-sample.xpi'); +var NORMAL_EXTENSION = path.join(__dirname, + '../../lib/test/data/firefox/sample.xpi'); + +var JETPACK_EXTENSION_ID = 'jid1-EaXX7k0wwiZR7w@jetpack'; +var NORMAL_EXTENSION_ID = 'sample@seleniumhq.org'; + + +describe('extension', function() { + it('can install a jetpack xpi file', function() { + return io.tmpDir().then(function(dir) { + return extension.install(JETPACK_EXTENSION, dir).then(function(id) { + assert.equal(JETPACK_EXTENSION_ID, id); + var file = path.join(dir, id + '.xpi'); + assert.ok(fs.existsSync(file), 'no such file: ' + file); + assert.ok(!fs.statSync(file).isDirectory()); + + var copiedSha1 = crypto.createHash('sha1') + .update(fs.readFileSync(file)) + .digest('hex'); + + var goldenSha1 = crypto.createHash('sha1') + .update(fs.readFileSync(JETPACK_EXTENSION)) + .digest('hex'); + + assert.equal(copiedSha1, goldenSha1); + }); + }); + }); + + it('can install a normal xpi file', function() { + return io.tmpDir().then(function(dir) { + return extension.install(NORMAL_EXTENSION, dir).then(function(id) { + assert.equal(NORMAL_EXTENSION_ID, id); + + var file = path.join(dir, NORMAL_EXTENSION_ID); + assert.ok(fs.statSync(file).isDirectory()); + + assert.ok(fs.existsSync(path.join(file, 'chrome.manifest'))); + assert.ok(fs.existsSync(path.join(file, 'content/overlay.xul'))); + assert.ok(fs.existsSync(path.join(file, 'content/overlay.js'))); + assert.ok(fs.existsSync(path.join(file, 'install.rdf'))); + }); + }); + }); + + it('can install an extension from a directory', function() { + return io.tmpDir().then(function(srcDir) { + var buf = fs.readFileSync(NORMAL_EXTENSION); + new AdmZip(buf).extractAllTo(srcDir, true); + return io.tmpDir().then(function(dstDir) { + return extension.install(srcDir, dstDir).then(function(id) { + assert.equal(NORMAL_EXTENSION_ID, id); + + var dir = path.join(dstDir, NORMAL_EXTENSION_ID); + + assert.ok(fs.existsSync(path.join(dir, 'chrome.manifest'))); + assert.ok(fs.existsSync(path.join(dir, 'content/overlay.xul'))); + assert.ok(fs.existsSync(path.join(dir, 'content/overlay.js'))); + assert.ok(fs.existsSync(path.join(dir, 'install.rdf'))); + }); + }); + }); + }); +}); diff --git a/test/firefox/firefox_test.js b/test/firefox/firefox_test.js new file mode 100644 index 0000000..0a32940 --- /dev/null +++ b/test/firefox/firefox_test.js @@ -0,0 +1,183 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 path = require('path'); + +var firefox = require('../../firefox'), + io = require('../../io'), + test = require('../../lib/test'), + assert = require('../../testing/assert'); + + +var JETPACK_EXTENSION = path.join(__dirname, + '../../lib/test/data/firefox/jetpack-sample.xpi'); +var NORMAL_EXTENSION = path.join(__dirname, + '../../lib/test/data/firefox/sample.xpi'); + + +test.suite(function(env) { + describe('firefox', function() { + describe('Options', function() { + var driver; + + test.beforeEach(function() { + driver = null; + }); + + test.afterEach(function() { + if (driver) { + driver.quit(); + } + }); + + test.it('can start Firefox with custom preferences', function() { + var profile = new firefox.Profile(); + profile.setPreference('general.useragent.override', 'foo;bar'); + + var options = new firefox.Options().setProfile(profile); + + driver = env.builder(). + setFirefoxOptions(options). + build(); + + driver.get('data:text/html,
        content
        '); + + var userAgent = driver.executeScript( + 'return window.navigator.userAgent'); + assert(userAgent).equalTo('foo;bar'); + }); + + test.it('can start Firefox with a jetpack extension', function() { + var profile = new firefox.Profile(); + profile.addExtension(JETPACK_EXTENSION); + + var options = new firefox.Options().setProfile(profile); + + driver = env.builder(). + setFirefoxOptions(options). + build(); + + loadJetpackPage(driver, + 'data:text/html;charset=UTF-8,
        content
        '); + assert(driver.findElement({id: 'jetpack-sample-banner'}).getText()) + .equalTo('Hello, world!'); + }); + + test.it('can start Firefox with a normal extension', function() { + var profile = new firefox.Profile(); + profile.addExtension(NORMAL_EXTENSION); + + var options = new firefox.Options().setProfile(profile); + + driver = env.builder(). + setFirefoxOptions(options). + build(); + + driver.get('data:text/html,
        content
        '); + assert(driver.findElement({id: 'sample-extension-footer'}).getText()) + .equalTo('Goodbye'); + }); + + test.it('can start Firefox with multiple extensions', function() { + var profile = new firefox.Profile(); + profile.addExtension(JETPACK_EXTENSION); + profile.addExtension(NORMAL_EXTENSION); + + var options = new firefox.Options().setProfile(profile); + + driver = env.builder(). + setFirefoxOptions(options). + build(); + + loadJetpackPage(driver, + 'data:text/html;charset=UTF-8,
        content
        '); + assert(driver.findElement({id: 'jetpack-sample-banner'}).getText()) + .equalTo('Hello, world!'); + assert(driver.findElement({id: 'sample-extension-footer'}).getText()) + .equalTo('Goodbye'); + }); + + function loadJetpackPage(driver, url) { + // On linux the jetpack extension does not always run the first time + // we load a page. If this happens, just reload the page (a simple + // refresh doesn't appear to work). + driver.wait(function() { + driver.get(url); + return driver.isElementPresent({id: 'jetpack-sample-banner'}); + }, 3000); + } + }); + + describe('profile management', function() { + var driver; + + test.beforeEach(function() { + driver = null; + }); + + test.afterEach(function() { + if (driver) { + driver.quit(); + } + }); + + test.ignore(env.isRemote). + it('deletes the temp profile on quit', function() { + driver = env.builder().build(); + + var profilePath = driver.call(function() { + var path = driver.profilePath_; + assert(io.exists(path)).isTrue(); + return path; + }); + + return driver.quit().then(function() { + driver = null; + return profilePath; + }).then(function(path) { + assert(io.exists(path)).isFalse(); + }); + }); + }); + + describe('binary management', function() { + var driver1, driver2; + + test.ignore(env.isRemote). + it('can start multiple sessions with single binary instance', function() { + var options = new firefox.Options().setBinary(new firefox.Binary); + env.builder().setFirefoxOptions(options); + driver1 = env.builder().build(); + driver2 = env.builder().build(); + // Ok if this doesn't fail. + }); + + test.afterEach(function() { + if (driver1) { + driver1.quit(); + } + + if (driver2) { + driver2.quit(); + } + }); + }); + + }); +}, {browsers: ['firefox']}); diff --git a/test/firefox/profile_test.js b/test/firefox/profile_test.js new file mode 100644 index 0000000..feaa42f --- /dev/null +++ b/test/firefox/profile_test.js @@ -0,0 +1,187 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 AdmZip = require('adm-zip'), + assert = require('assert'), + fs = require('fs'), + path = require('path'); + +var promise = require('../..').promise, + Profile = require('../../firefox/profile').Profile, + decode = require('../../firefox/profile').decode, + loadUserPrefs = require('../../firefox/profile').loadUserPrefs, + io = require('../../io'), + it = require('../../testing').it; + + +var JETPACK_EXTENSION = path.join(__dirname, + '../../lib/test/data/firefox/jetpack-sample.xpi'); +var NORMAL_EXTENSION = path.join(__dirname, + '../../lib/test/data/firefox/sample.xpi'); + +var JETPACK_EXTENSION_ID = 'jid1-EaXX7k0wwiZR7w@jetpack.xpi'; +var NORMAL_EXTENSION_ID = 'sample@seleniumhq.org'; +var WEBDRIVER_EXTENSION_ID = 'fxdriver@googlecode.com'; + + + +describe('Profile', function() { + describe('setPreference', function() { + it('allows setting custom properties', function() { + var profile = new Profile(); + assert.equal(undefined, profile.getPreference('foo')); + + profile.setPreference('foo', 'bar'); + assert.equal('bar', profile.getPreference('foo')); + }); + + it('allows overriding mutable properties', function() { + var profile = new Profile(); + assert.equal('about:blank', profile.getPreference('browser.newtab.url')); + + profile.setPreference('browser.newtab.url', 'http://www.example.com'); + assert.equal('http://www.example.com', + profile.getPreference('browser.newtab.url')); + }); + + it('throws if setting a frozen preference', function() { + var profile = new Profile(); + assert.throws(function() { + profile.setPreference('app.update.auto', true); + }); + }); + }); + + describe('writeToDisk', function() { + it('copies template directory recursively', function() { + var templateDir; + return io.tmpDir().then(function(td) { + templateDir = td; + var foo = path.join(templateDir, 'foo'); + fs.writeFileSync(foo, 'Hello, world'); + + var bar = path.join(templateDir, 'subfolder/bar'); + fs.mkdirSync(path.dirname(bar)); + fs.writeFileSync(bar, 'Goodbye, world!'); + + return new Profile(templateDir).writeToDisk(); + }).then(function(profileDir) { + assert.notEqual(profileDir, templateDir); + + assert.equal('Hello, world', + fs.readFileSync(path.join(profileDir, 'foo'))); + assert.equal('Goodbye, world!', + fs.readFileSync(path.join(profileDir, 'subfolder/bar'))); + }); + }); + + it('does not copy lock files', function() { + return io.tmpDir().then(function(dir) { + fs.writeFileSync(path.join(dir, 'parent.lock'), 'lock'); + fs.writeFileSync(path.join(dir, 'lock'), 'lock'); + fs.writeFileSync(path.join(dir, '.parentlock'), 'lock'); + return new Profile(dir).writeToDisk(); + }).then(function(dir) { + assert.ok(fs.existsSync(dir)); + assert.ok(!fs.existsSync(path.join(dir, 'parent.lock'))); + assert.ok(!fs.existsSync(path.join(dir, 'lock'))); + assert.ok(!fs.existsSync(path.join(dir, '.parentlock'))); + }); + }); + + describe('user.js', function() { + + it('writes defaults', function() { + return new Profile().writeToDisk().then(function(dir) { + return loadUserPrefs(path.join(dir, 'user.js')); + }).then(function(prefs) { + // Just check a few. + assert.equal(false, prefs['app.update.auto']); + assert.equal(true, prefs['browser.EULA.override']); + assert.equal(false, prefs['extensions.update.enabled']); + assert.equal('about:blank', prefs['browser.newtab.url']); + assert.equal(30, prefs['dom.max_script_run_time']); + }); + }); + + it('merges template user.js into preferences', function() { + return io.tmpDir().then(function(dir) { + fs.writeFileSync(path.join(dir, 'user.js'), [ + 'user_pref("browser.newtab.url", "http://www.example.com")', + 'user_pref("dom.max_script_run_time", 1234)' + ].join('\n')); + + return new Profile(dir).writeToDisk(); + }).then(function(profile) { + return loadUserPrefs(path.join(profile, 'user.js')); + }).then(function(prefs) { + assert.equal('http://www.example.com', prefs['browser.newtab.url']); + assert.equal(1234, prefs['dom.max_script_run_time']); + }); + }); + + it('ignores frozen preferences when merging template user.js', + function() { + return io.tmpDir().then(function(dir) { + fs.writeFileSync(path.join(dir, 'user.js'), + 'user_pref("app.update.auto", true)'); + return new Profile(dir).writeToDisk(); + }).then(function(profile) { + return loadUserPrefs(path.join(profile, 'user.js')); + }).then(function(prefs) { + assert.equal(false, prefs['app.update.auto']); + }); + }); + }); + + describe('extensions', function() { + it('are copied into new profile directory', function() { + var profile = new Profile(); + profile.addExtension(JETPACK_EXTENSION); + profile.addExtension(NORMAL_EXTENSION); + + return profile.writeToDisk().then(function(dir) { + dir = path.join(dir, 'extensions'); + assert.ok(fs.existsSync(path.join(dir, JETPACK_EXTENSION_ID))); + assert.ok(fs.existsSync(path.join(dir, NORMAL_EXTENSION_ID))); + assert.ok(fs.existsSync(path.join(dir, WEBDRIVER_EXTENSION_ID))); + }); + }); + }); + }); + + describe('encode', function() { + it('excludes the bundled WebDriver extension', function() { + return new Profile().encode().then(function(data) { + return decode(data); + }).then(function(dir) { + assert.ok(fs.existsSync(path.join(dir, 'user.js'))); + assert.ok(fs.existsSync(path.join(dir, 'extensions'))); + return loadUserPrefs(path.join(dir, 'user.js')); + }).then(function(prefs) { + // Just check a few. + assert.equal(false, prefs['app.update.auto']); + assert.equal(true, prefs['browser.EULA.override']); + assert.equal(false, prefs['extensions.update.enabled']); + assert.equal('about:blank', prefs['browser.newtab.url']); + assert.equal(30, prefs['dom.max_script_run_time']); + }); + }); + }); +}); diff --git a/test/http/http_test.js b/test/http/http_test.js new file mode 100644 index 0000000..6298967 --- /dev/null +++ b/test/http/http_test.js @@ -0,0 +1,181 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 assert = require('assert'); +var http = require('http'); +var url = require('url'); + +var HttpClient = require('../../http').HttpClient; +var HttpRequest = require('../../_base').require('webdriver.http.Request'); +var Server = require('../../lib/test/httpserver').Server; +var promise = require('../..').promise; +var test = require('../../lib/test'); + +describe('HttpClient', function() { + this.timeout(4 * 1000); + + var server = new Server(function(req, res) { + if (req.method == 'GET' && req.url == '/echo') { + res.writeHead(200, req.headers); + res.end(); + + } else if (req.method == 'GET' && req.url == '/redirect') { + res.writeHead(303, {'Location': server.url('/hello')}); + res.end(); + + } else if (req.method == 'GET' && req.url == '/hello') { + res.writeHead(200, {'content-type': 'text/plain'}); + res.end('hello, world!'); + + } else if (req.method == 'GET' && req.url == '/badredirect') { + res.writeHead(303, {}); + res.end(); + + } else if (req.method == 'GET' && req.url == '/protected') { + var denyAccess = function() { + res.writeHead(401, {'WWW-Authenticate': 'Basic realm="test"'}); + res.end('Access denied'); + }; + + var basicAuthRegExp = /^\s*basic\s+([a-z0-9\-\._~\+\/]+)=*\s*$/i + var auth = req.headers.authorization; + var match = basicAuthRegExp.exec(auth || ''); + if (!match) { + denyAccess(); + return; + } + + var userNameAndPass = new Buffer(match[1], 'base64').toString(); + var parts = userNameAndPass.split(':', 2); + if (parts[0] !== 'genie' && parts[1] !== 'bottle') { + denyAccess(); + return; + } + + res.writeHead(200, {'content-type': 'text/plain'}); + res.end('Access granted!'); + + } else if (req.method == 'GET' && req.url == '/proxy') { + res.writeHead(200, req.headers); + res.end(); + + } else if (req.method == 'GET' && req.url == '/proxy/redirect') { + res.writeHead(303, {'Location': '/proxy'}); + res.end(); + + } else { + res.writeHead(404, {}); + res.end(); + } + }); + + test.before(function() { + return server.start(); + }); + + test.after(function() { + return server.stop(); + }); + + test.it('can send a basic HTTP request', function() { + var request = new HttpRequest('GET', '/echo'); + request.headers['Foo'] = 'Bar'; + + var agent = new http.Agent(); + agent.maxSockets = 1; // Only making 1 request. + + var client = new HttpClient(server.url(), agent); + return promise.checkedNodeCall(client.send.bind(client, request)) + .then(function(response) { + assert.equal(200, response.status); + assert.equal( + 'application/json; charset=utf-8', response.headers['accept']); + assert.equal('Bar', response.headers['foo']); + assert.equal('0', response.headers['content-length']); + assert.equal('keep-alive', response.headers['connection']); + assert.equal(server.host(), response.headers['host']); + }); + }); + + test.it('can use basic auth', function() { + var parsed = url.parse(server.url()); + parsed.auth = 'genie:bottle'; + + var client = new HttpClient(url.format(parsed)); + var request = new HttpRequest('GET', '/protected'); + return promise.checkedNodeCall(client.send.bind(client, request)) + .then(function(response) { + assert.equal(200, response.status); + assert.equal('text/plain', response.headers['content-type']); + assert.equal('Access granted!', response.body); + }); + }); + + test.it('fails requests missing required basic auth', function() { + var client = new HttpClient(server.url()); + var request = new HttpRequest('GET', '/protected'); + return promise.checkedNodeCall(client.send.bind(client, request)) + .then(function(response) { + assert.equal(401, response.status); + assert.equal('Access denied', response.body); + }); + }); + + test.it('automatically follows redirects', function() { + var request = new HttpRequest('GET', '/redirect'); + var client = new HttpClient(server.url()); + return promise.checkedNodeCall(client.send.bind(client, request)) + .then(function(response) { + assert.equal(200, response.status); + assert.equal('text/plain', response.headers['content-type']); + assert.equal('hello, world!', response.body); + }); + }); + + test.it('handles malformed redirect responses', function() { + var request = new HttpRequest('GET', '/badredirect'); + var client = new HttpClient(server.url()); + return promise.checkedNodeCall(client.send.bind(client, request)) + .thenCatch(function(err) { + assert.ok(/Failed to parse "Location"/.test(err.message), + 'Not the expected error: ' + err.message); + }); + }); + + test.it('proxies requests through the webdriver proxy', function() { + var request = new HttpRequest('GET', '/proxy'); + var client = new HttpClient( + 'http://another.server.com', undefined, server.url()); + return promise.checkedNodeCall(client.send.bind(client, request)) + .then(function(response) { + assert.equal(200, response.status); + assert.equal('another.server.com', response.headers['host']); + }); + }); + + test.it( + 'proxies requests through the webdriver proxy on redirect', function() { + var request = new HttpRequest('GET', '/proxy/redirect'); + var client = new HttpClient( + 'http://another.server.com', undefined, server.url()); + return promise.checkedNodeCall(client.send.bind(client, request)) + .then(function(response) { + assert.equal(200, response.status); + assert.equal('another.server.com', response.headers['host']); + }); + }); +}); diff --git a/test/http/util_test.js b/test/http/util_test.js index 37835b4..4876297 100644 --- a/test/http/util_test.js +++ b/test/http/util_test.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -125,12 +127,12 @@ describe('selenium-webdriver/http/util', function() { var isReady = util.waitForServer(baseUrl, 200). then(function() { done('Did not expect to succeed'); }). then(null, function(e) { - assert.equal(err, e); + assert.equal('cancelled!', e.message); }). then(function() { done(); }, done); setTimeout(function() { - isReady.cancel(err); + isReady.cancel('cancelled!'); }, 50); }); }); @@ -165,16 +167,15 @@ describe('selenium-webdriver/http/util', function() { it('can cancel wait', function(done) { responseCode = 404; - var err = Error('cancelled!'); var isReady = util.waitForUrl(baseUrl, 200). then(function() { done('Did not expect to succeed'); }). then(null, function(e) { - assert.equal(err, e); + assert.equal('cancelled!', e.message); }). then(function() { done(); }, done); setTimeout(function() { - isReady.cancel(err); + isReady.cancel('cancelled!'); }, 50); }); }); diff --git a/test/io_test.js b/test/io_test.js new file mode 100644 index 0000000..2fe4475 --- /dev/null +++ b/test/io_test.js @@ -0,0 +1,227 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 assert = require('assert'), + fs = require('fs'), + path = require('path'), + tmp = require('tmp'); + +var io = require('../io'), + before = require('../testing').before, + beforeEach = require('../testing').beforeEach, + it = require('../testing').it; + + +describe('io', function() { + describe('copy', function() { + var tmpDir; + + before(function() { + return io.tmpDir().then(function(d) { + tmpDir = d; + + fs.writeFileSync(path.join(d, 'foo'), 'Hello, world'); + fs.symlinkSync(path.join(d, 'foo'), path.join(d, 'symlinked-foo')); + }); + }); + + it('can copy one file to another', function() { + return io.tmpFile().then(function(f) { + return io.copy(path.join(tmpDir, 'foo'), f).then(function(p) { + assert.equal(p, f); + assert.equal('Hello, world', fs.readFileSync(p)); + }); + }); + }); + + it('can copy symlink to destination', function() { + return io.tmpFile().then(function(f) { + return io.copy(path.join(tmpDir, 'symlinked-foo'), f).then(function(p) { + assert.equal(p, f); + assert.equal('Hello, world', fs.readFileSync(p)); + }); + }); + }); + + it('fails if given a directory as a source', function() { + return io.tmpFile().then(function(f) { + return io.copy(tmpDir, f); + }).then(function() { + throw Error('Should have failed with a type error'); + }, function() { + // Do nothing; expected. + }); + }); + }); + + describe('copyDir', function() { + it('copies recursively', function() { + return io.tmpDir().then(function(dir) { + fs.writeFileSync(path.join(dir, 'file1'), 'hello'); + fs.mkdirSync(path.join(dir, 'sub')); + fs.mkdirSync(path.join(dir, 'sub/folder')); + fs.writeFileSync(path.join(dir, 'sub/folder/file2'), 'goodbye'); + + return io.tmpDir().then(function(dst) { + return io.copyDir(dir, dst).then(function(ret) { + assert.equal(dst, ret); + + assert.equal('hello', + fs.readFileSync(path.join(dst, 'file1'))); + assert.equal('goodbye', + fs.readFileSync(path.join(dst, 'sub/folder/file2'))); + }); + }); + }); + }); + + it('creates destination dir if necessary', function() { + return io.tmpDir().then(function(srcDir) { + fs.writeFileSync(path.join(srcDir, 'foo'), 'hi'); + return io.tmpDir().then(function(dstDir) { + return io.copyDir(srcDir, path.join(dstDir, 'sub')); + }); + }).then(function(p) { + assert.equal('sub', path.basename(p)); + assert.equal('hi', fs.readFileSync(path.join(p, 'foo'))); + }); + }); + + it('supports regex exclusion filter', function() { + return io.tmpDir().then(function(src) { + fs.writeFileSync(path.join(src, 'foo'), 'a'); + fs.writeFileSync(path.join(src, 'bar'), 'b'); + fs.writeFileSync(path.join(src, 'baz'), 'c'); + fs.mkdirSync(path.join(src, 'sub')); + fs.writeFileSync(path.join(src, 'sub/quux'), 'd'); + fs.writeFileSync(path.join(src, 'sub/quot'), 'e'); + + return io.tmpDir().then(function(dst) { + return io.copyDir(src, dst, /(bar|quux)/); + }); + }).then(function(dir) { + assert.equal('a', fs.readFileSync(path.join(dir, 'foo'))); + assert.equal('c', fs.readFileSync(path.join(dir, 'baz'))); + assert.equal('e', fs.readFileSync(path.join(dir, 'sub/quot'))); + + assert.ok(!fs.existsSync(path.join(dir, 'bar'))); + assert.ok(!fs.existsSync(path.join(dir, 'sub/quux'))); + }); + }); + + it('supports exclusion filter function', function() { + return io.tmpDir().then(function(src) { + fs.writeFileSync(path.join(src, 'foo'), 'a'); + fs.writeFileSync(path.join(src, 'bar'), 'b'); + fs.writeFileSync(path.join(src, 'baz'), 'c'); + fs.mkdirSync(path.join(src, 'sub')); + fs.writeFileSync(path.join(src, 'sub/quux'), 'd'); + fs.writeFileSync(path.join(src, 'sub/quot'), 'e'); + + return io.tmpDir().then(function(dst) { + return io.copyDir(src, dst, function(f) { + return f !== path.join(src, 'foo') + && f !== path.join(src, 'sub/quot'); + }); + }); + }).then(function(dir) { + assert.equal('b', fs.readFileSync(path.join(dir, 'bar'))); + assert.equal('c', fs.readFileSync(path.join(dir, 'baz'))); + assert.equal('d', fs.readFileSync(path.join(dir, 'sub/quux'))); + + assert.ok(!fs.existsSync(path.join(dir, 'foo'))); + assert.ok(!fs.existsSync(path.join(dir, 'sub/quot'))); + }); + }); + }); + + describe('exists', function() { + var dir; + + before(function() { + return io.tmpDir().then(function(d) { + dir = d; + }); + }); + + it('works for directories', function() { + return io.exists(dir).then(assert.ok); + }); + + it('works for files', function() { + var file = path.join(dir, 'foo'); + fs.writeFileSync(file, ''); + return io.exists(file).then(assert.ok); + }); + + it('does not return a rejected promise if file does not exist', function() { + return io.exists(path.join(dir, 'not-there')).then(function(exists) { + assert.ok(!exists); + }); + }); + }); + + describe('unlink', function() { + var dir; + + before(function() { + return io.tmpDir().then(function(d) { + dir = d; + }); + }); + + it('silently succeeds if the path does not exist', function() { + return io.unlink(path.join(dir, 'not-there')); + }); + + it('deletes files', function() { + var file = path.join(dir, 'foo'); + fs.writeFileSync(file, ''); + return io.exists(file).then(assert.ok).then(function() { + return io.unlink(file); + }).then(function() { + return io.exists(file); + }).then(function(exists) { + return assert.ok(!exists); + }); + }); + }); + + describe('rmDir', function() { + it('succeeds if the designated directory does not exist', function() { + return io.tmpDir().then(function(d) { + return io.rmDir(path.join(d, 'i/do/not/exist')); + }); + }); + + it('deletes recursively', function() { + return io.tmpDir().then(function(dir) { + fs.writeFileSync(path.join(dir, 'file1'), 'hello'); + fs.mkdirSync(path.join(dir, 'sub')); + fs.mkdirSync(path.join(dir, 'sub/folder')); + fs.writeFileSync(path.join(dir, 'sub/folder/file2'), 'goodbye'); + + return io.rmDir(dir).then(function() { + assert.ok(!fs.existsSync(dir)); + assert.ok(!fs.existsSync(path.join(dir, 'sub/folder/file2'))); + }); + }); + }); + }); +}); diff --git a/test/logging_test.js b/test/logging_test.js new file mode 100644 index 0000000..fb8cdb2 --- /dev/null +++ b/test/logging_test.js @@ -0,0 +1,167 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 Browser = require('..').Browser, + By = require('..').By, + logging = require('..').logging, + assert = require('../testing/assert'), + test = require('../lib/test'); + +test.suite(function(env) { + // Logging API has numerous issues with PhantomJS: + // - does not support adjusting log levels for type "browser". + // - does not return proper log level for "browser" messages. + // - does not delete logs after retrieval + // Logging API is not supported in IE. + // Tests depend on opening data URLs, which is broken in Safari (issue 7586) + test.ignore(env.browsers(Browser.PHANTOM_JS, Browser.IE, Browser.SAFARI)). + describe('logging', function() { + var driver; + + test.beforeEach(function() { + driver = null; + }); + + test.afterEach(function() { + if (driver) { + driver.quit(); + } + }); + + test.it('can be disabled', function() { + var prefs = new logging.Preferences(); + prefs.setLevel(logging.Type.BROWSER, logging.Level.OFF); + + driver = env.builder() + .setLoggingPrefs(prefs) + .build(); + + driver.get(dataUrl( + '')); + driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) { + assert(entries.length).equalTo(0); + }); + }); + + // Firefox does not capture JS error console log messages. + test.ignore(env.browsers(Browser.FIREFOX)). + it('can be turned down', function() { + var prefs = new logging.Preferences(); + prefs.setLevel(logging.Type.BROWSER, logging.Level.SEVERE); + + driver = env.builder() + .setLoggingPrefs(prefs) + .build(); + + driver.get(dataUrl( + '')); + driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) { + assert(entries.length).equalTo(1); + assert(entries[0].level.name).equalTo('SEVERE'); + assert(entries[0].message).endsWith('and this is an error'); + }); + }); + + // Firefox does not capture JS error console log messages. + test.ignore(env.browsers(Browser.FIREFOX)). + it('can be made verbose', function() { + var prefs = new logging.Preferences(); + prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG); + + driver = env.builder() + .setLoggingPrefs(prefs) + .build(); + + driver.get(dataUrl( + '')); + driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) { + assert(entries.length).equalTo(3); + assert(entries[0].level.name).equalTo('DEBUG'); + assert(entries[0].message).endsWith('hello'); + + assert(entries[1].level.name).equalTo('WARNING'); + assert(entries[1].message).endsWith('this is a warning'); + + assert(entries[2].level.name).equalTo('SEVERE'); + assert(entries[2].message).endsWith('and this is an error'); + }); + }); + + // Firefox does not capture JS error console log messages. + test.ignore(env.browsers(Browser.FIREFOX)). + it('clears records after retrieval', function() { + var prefs = new logging.Preferences(); + prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG); + + driver = env.builder() + .setLoggingPrefs(prefs) + .build(); + + driver.get(dataUrl( + '')); + driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) { + assert(entries.length).equalTo(3); + }); + driver.manage().logs().get(logging.Type.BROWSER).then(function(entries) { + assert(entries.length).equalTo(0); + }); + }); + + test.it('does not mix log types', function() { + var prefs = new logging.Preferences(); + prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG); + prefs.setLevel(logging.Type.DRIVER, logging.Level.SEVERE); + + driver = env.builder() + .setLoggingPrefs(prefs) + .build(); + + driver.get(dataUrl( + '')); + driver.manage().logs().get(logging.Type.DRIVER).then(function(entries) { + assert(entries.length).equalTo(0); + }); + }); + }); + + function dataUrl(var_args) { + return 'data:text/html,' + + Array.prototype.slice.call(arguments, 0).join(''); + } +}); diff --git a/test/net/portprober_test.js b/test/net/portprober_test.js index ae58cb6..03a2f7a 100644 --- a/test/net/portprober_test.js +++ b/test/net/portprober_test.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; diff --git a/test/page_loading_test.js b/test/page_loading_test.js index 812faae..df67d76 100644 --- a/test/page_loading_test.js +++ b/test/page_loading_test.js @@ -1,34 +1,42 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 By = require('..').By, +var Browser = require('..').Browser, + By = require('..').By, ErrorCode = require('..').error.ErrorCode, + until = require('..').until, assert = require('../testing/assert'), test = require('../lib/test'), - Browser = test.Browser, Pages = test.Pages; test.suite(function(env) { - var browsers = env.browsers, - waitForTitleToBe = env.waitForTitleToBe; + var browsers = env.browsers; var driver; - beforeEach(function() { driver = env.driver; }); + test.before(function() { + driver = env.builder().build(); + }); + + test.after(function() { + driver.quit(); + }); test.it('should wait for document to be loaded', function() { driver.get(Pages.simpleTestPage); @@ -41,8 +49,7 @@ test.suite(function(env) { assert(driver.getTitle()).equalTo('We Arrive Here'); }); - test.ignore(browsers(Browser.ANDROID)).it('should follow meta redirects', - function() { + test.it('should follow meta redirects', function() { driver.get(Pages.metaRedirectPage); assert(driver.getTitle()).equalTo('We Arrive Here'); }); @@ -53,7 +60,7 @@ test.suite(function(env) { driver.findElement(By.id('id1')); }); - test.ignore(browsers(Browser.ANDROID, Browser.IOS)). + test.ignore(browsers(Browser.IPAD, Browser.IPHONE)). it('should wait for all frames to load in a frameset', function() { driver.get(Pages.framesetPage); driver.switchTo().frame(0); @@ -69,12 +76,12 @@ test.suite(function(env) { }); }); - test.ignore(browsers(Browser.ANDROID, Browser.SAFARI)). + test.ignore(browsers(Browser.SAFARI)). it('should be able to navigate back in browser history', function() { driver.get(Pages.formPage); driver.findElement(By.id('imageButton')).click(); - waitForTitleToBe('We Arrive Here'); + driver.wait(until.titleIs('We Arrive Here'), 5000); driver.navigate().back(); assert(driver.getTitle()).equalTo('We Leave From Here'); @@ -85,27 +92,29 @@ test.suite(function(env) { driver.get(Pages.xhtmlTestPage); driver.findElement(By.name('sameWindow')).click(); - waitForTitleToBe('This page has iframes'); + driver.wait(until.titleIs('This page has iframes'), 5000); driver.navigate().back(); assert(driver.getTitle()).equalTo('XHTML Test Page'); }); - test.ignore(browsers(Browser.ANDROID, Browser.SAFARI)). + test.ignore(browsers(Browser.SAFARI)). it('should be able to navigate forwards in browser history', function() { driver.get(Pages.formPage); driver.findElement(By.id('imageButton')).click(); - waitForTitleToBe('We Arrive Here'); + driver.wait(until.titleIs('We Arrive Here'), 5000); driver.navigate().back(); - waitForTitleToBe('We Leave From Here'); + driver.wait(until.titleIs('We Leave From Here'), 5000); driver.navigate().forward(); - waitForTitleToBe('We Arrive Here'); + driver.wait(until.titleIs('We Arrive Here'), 5000); }); - test.it('should be able to refresh a page', function() { + // PhantomJS 2.0 does not properly reload pages on refresh. + test.ignore(browsers(Browser.PHANTOM_JS)). + it('should be able to refresh a page', function() { driver.get(Pages.xhtmlTestPage); driver.navigate().refresh(); @@ -123,12 +132,12 @@ test.suite(function(env) { // Only implemented in Firefox. test.ignore(browsers( - Browser.ANDROID, Browser.CHROME, Browser.IE, - Browser.IOS, + Browser.IPAD, + Browser.IPHONE, Browser.OPERA, - Browser.PHANTOMJS, + Browser.PHANTOM_JS, Browser.SAFARI)). it('should timeout if page load timeout is set', function() { driver.call(function() { @@ -137,7 +146,12 @@ test.suite(function(env) { then(function() { throw Error('Should have timed out on page load'); }, function(e) { - assert(e.code).equalTo(ErrorCode.SCRIPT_TIMEOUT); + // The FirefoxDriver returns TIMEOUT directly, where as the + // java server returns SCRIPT_TIMEOUT (bug?). + if (e.code !== ErrorCode.SCRIPT_TIMEOUT && + e.code !== ErrorCode.TIMEOUT) { + throw Error('Unexpected error response: ' + e); + } }); }).then(resetPageLoad, function(err) { resetPageLoad().thenFinally(function() { diff --git a/test/phantomjs/execute_phantomjs_test.js b/test/phantomjs/execute_phantomjs_test.js new file mode 100644 index 0000000..22a9a22 --- /dev/null +++ b/test/phantomjs/execute_phantomjs_test.js @@ -0,0 +1,73 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 assert = require('assert'); +var path = require('path'); +var test = require('../../lib/test'); + +test.suite(function(env) { + var driver; + + test.before(function() { + driver = env.builder().build(); + }); + + test.after(function() { + driver.quit(); + }); + + var testPageUrl = + 'data:text/html,

        ' + path.basename(__filename) + '

        '; + + test.beforeEach(function() { + driver.get(testPageUrl); + }); + + describe('phantomjs.Driver', function() { + describe('#executePhantomJS()', function() { + + test.it('can execute scripts using PhantomJS API', function() { + return driver.executePhantomJS('return this.url;').then(function(url) { + assert.equal(testPageUrl, decodeURIComponent(url)); + }); + }); + + test.it('can execute scripts as functions', function() { + driver.executePhantomJS(function(a, b) { + return a + b; + }, 1, 2).then(function(result) { + assert.equal(3, result); + }); + }); + + test.it('can manipulate the current page', function() { + driver.manage().addCookie('foo', 'bar'); + driver.manage().getCookie('foo').then(function(cookie) { + assert.equal('bar', cookie.value); + }); + driver.executePhantomJS(function() { + this.clearCookies(); + }); + driver.manage().getCookie('foo').then(function(cookie) { + assert.equal(null, cookie); + }); + }); + }); + }); +}, {browsers: ['phantomjs']}); diff --git a/test/promise_aplus_test.js b/test/promise_aplus_test.js new file mode 100644 index 0000000..1f59fdd --- /dev/null +++ b/test/promise_aplus_test.js @@ -0,0 +1,83 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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'; + +describe('Promises/A+ Compliance Tests', function() { + var promise = require('../_base').require('webdriver.promise'); + + // The promise spec does not define behavior for unhandled rejections and + // assumes they are effectively swallowed. This is not the case with our + // implementation, so we have to disable error propagation to test that the + // rest of our behavior is compliant. + // We run the tests with a separate instance of the control flow to ensure + // disablign error propagation does not impact other tests. + var flow = new promise.ControlFlow(); + flow.setPropagateUnhandledRejections(false); + + function startsWith(str, prefix) { + return str.indexOf(prefix) === 0; + } + + function endsWith(str, suffix) { + let len = str.length - suffix.length; + return len >= 0 && str.indexOf(suffix, len) === len; + } + + // Skip the tests in 2.2.6.1/2. We are not compliant in these scenarios. + var realDescribe = global.describe; + global.describe = function(name, fn) { + realDescribe(name, function() { + var prefix = 'Promises/A+ Compliance Tests 2.2.6: ' + + '`then` may be called multiple times on the same promise.'; + var suffix = 'even when one handler is added inside another handler'; + if (startsWith(this.fullTitle(), prefix) + && endsWith(this.fullTitle(), suffix)) { + var realSpecify = global.specify; + try { + global.specify = function(name) { + realSpecify(name); + }; + fn(); + } finally { + global.specify = realSpecify; + } + } else { + fn(); + } + }); + }; + + require('promises-aplus-tests').mocha({ + resolved: function(value) { + return new promise.Promise((fulfill) => fulfill(value), flow); + }, + rejected: function(error) { + return new promise.Promise((_, reject) => reject(error), flow); + }, + deferred: function() { + var d = new promise.Deferred(flow); + return { + resolve: d.fulfill, + reject: d.reject, + promise: d.promise + }; + } + }); + + global.describe = realDescribe; +}); diff --git a/test/proxy_test.js b/test/proxy_test.js index 89d66dd..b7a894f 100644 --- a/test/proxy_test.js +++ b/test/proxy_test.js @@ -1,35 +1,35 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 http = require('http'), url = require('url'); -var promise = require('..').promise, +var Browser = require('..').Browser, + promise = require('..').promise, proxy = require('../proxy'), assert = require('../testing/assert'), test = require('../lib/test'), Server = require('../lib/test/httpserver').Server, - Browser = test.Browser, Pages = test.Pages; test.suite(function(env) { - env.autoCreateDriver = false; - function writeResponse(res, body, encoding, contentType) { res.writeHead(200, { 'Content-Length': Buffer.byteLength(body, encoding), @@ -78,23 +78,35 @@ test.suite(function(env) { ].join(''), 'utf8', 'text/html; charset=UTF-8'); }); - test.before(proxyServer.start.bind(proxyServer)); - test.before(helloServer.start.bind(helloServer)); - test.before(goodbyeServer.start.bind(helloServer)); + // Cannot pass start directly to mocha's before, as mocha will interpret the optional + // port parameter as an async callback parameter. + function mkStartFunc(server) { + return function() { + return server.start(); + }; + } + + + test.before(mkStartFunc(proxyServer)); + test.before(mkStartFunc(helloServer)); + test.before(mkStartFunc(goodbyeServer)); test.after(proxyServer.stop.bind(proxyServer)); test.after(helloServer.stop.bind(helloServer)); test.after(goodbyeServer.stop.bind(goodbyeServer)); - test.afterEach(env.dispose.bind(env)); + var driver; + test.beforeEach(function() { driver = null; }); + test.afterEach(function() { driver && driver.quit(); }); - test.ignore(env.browsers(Browser.SAFARI)). // Proxy support not implemented. + // Proxy support not implemented. + test.ignore(env.browsers(Browser.IE, Browser.OPERA, Browser.SAFARI)). describe('manual proxy settings', function() { // phantomjs 1.9.1 in webdriver mode does not appear to respect proxy // settings. - test.ignore(env.browsers(Browser.PHANTOMJS)). + test.ignore(env.browsers(Browser.PHANTOM_JS)). it('can configure HTTP proxy host', function() { - var driver = env.builder(). + driver = env.builder(). setProxy(proxy.manual({ http: proxyServer.host() })). @@ -107,9 +119,9 @@ test.suite(function(env) { }); // PhantomJS does not support bypassing the proxy for individual hosts. - test.ignore(env.browsers(Browser.PHANTOMJS)). + test.ignore(env.browsers(Browser.PHANTOM_JS)). it('can bypass proxy for specific hosts', function() { - var driver = env.builder(). + driver = env.builder(). setProxy(proxy.manual({ http: proxyServer.host(), bypass: helloServer.host() @@ -132,10 +144,11 @@ test.suite(function(env) { // PhantomJS does not support PAC file proxy configuration. // Safari does not support proxies. - test.ignore(env.browsers(Browser.PHANTOMJS, Browser.SAFARI)). + test.ignore(env.browsers( + Browser.IE, Browser.OPERA, Browser.PHANTOM_JS, Browser.SAFARI)). describe('pac proxy settings', function() { test.it('can configure proxy through PAC file', function() { - var driver = env.builder(). + driver = env.builder(). setProxy(proxy.pac(proxyServer.url('/proxy.pac'))). build(); diff --git a/test/remote_test.js b/test/remote_test.js new file mode 100644 index 0000000..c103d81 --- /dev/null +++ b/test/remote_test.js @@ -0,0 +1,72 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 assert = require('assert'); + +var promise = require('../').promise; +var remote = require('../remote'); + +describe('DriverService', function() { + describe('start()', function() { + var service; + + beforeEach(function() { + service = new remote.DriverService(process.execPath, { + port: 1234, + args: ['-e', 'process.exit(1)'] + }); + }); + + afterEach(function(done) { + service.kill().thenFinally(function() { + done(); + }); + }); + + it('fails if child-process dies', function(done) { + this.timeout(1000); + service.start(500) + .then(expectFailure.bind(null, done), verifyFailure.bind(null, done)); + }); + + it('failures propagate through control flow if child-process dies', + function(done) { + this.timeout(1000); + + promise.controlFlow().execute(function() { + promise.controlFlow().execute(function() { + return service.start(500); + }); + }) + .then(expectFailure.bind(null, done), verifyFailure.bind(null, done)); + }); + + function verifyFailure(done, e) { + try { + assert.ok(!(e instanceof promise.CancellationError)); + assert.equal('Server terminated early with status 1', e.message); + done(); + } catch (ex) { + done(ex); + } + } + + function expectFailure(done) { + done(Error('expected to fail')); + } + }); +}); diff --git a/test/stale_element_test.js b/test/stale_element_test.js index 81dacc7..5a9fa6f 100644 --- a/test/stale_element_test.js +++ b/test/stale_element_test.js @@ -1,33 +1,37 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 fail = require('assert').fail; -var By = require('..').By, +var Browser = require('..').Browser, + By = require('..').By, error = require('..').error, + until = require('..').until, assert = require('../testing/assert'), test = require('../lib/test'), - Browser = test.Browser, Pages = test.Pages; test.suite(function(env) { var driver; - beforeEach(function() { driver = env.driver; }); + test.before(function() { driver = env.builder().build(); }); + test.after(function() { driver.quit(); }); test.it( 'dynamically removing elements from the DOM trigger a ' + @@ -39,16 +43,7 @@ test.suite(function(env) { assert(toBeDeleted.isDisplayed()).isTrue(); driver.findElement(By.id('delete')).click(); - driver.wait(function() { - return toBeDeleted.isDisplayed(). - then(function() { return false; }). - then(null, function(e) { - if (e.code === error.ErrorCode.STALE_ELEMENT_REFERENCE) { - return true; - } - throw e; - }); - }, 5000, 'Element should be stale at this point'); + driver.wait(until.stalenessOf(toBeDeleted), 5000); }); test.it('an element found in a different frame is stale', function() { diff --git a/test/tag_name_test.js b/test/tag_name_test.js index c4f1672..d5e18a9 100644 --- a/test/tag_name_test.js +++ b/test/tag_name_test.js @@ -1,17 +1,19 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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'; @@ -21,9 +23,12 @@ var By = require('..').By, test.suite(function(env) { + var driver; + test.after(function() { driver.quit(); }); + test.it('should return lower case tag name', function() { - env.driver.get(test.Pages.formPage); - assert(env.driver.findElement(By.id('cheese')).getTagName()). - equalTo('input'); + driver = env.builder().build(); + driver.get(test.Pages.formPage); + assert(driver.findElement(By.id('cheese')).getTagName()).equalTo('input'); }); }); diff --git a/test/testing/index_test.js b/test/testing/index_test.js index bcd1d69..7a4b265 100644 --- a/test/testing/index_test.js +++ b/test/testing/index_test.js @@ -1,21 +1,24 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 assert = require('assert'); +var promise = require('../..').promise; var test = require('../../testing'); @@ -38,4 +41,140 @@ describe('Mocha Integration', function() { test.it('', function() { this.x = 2; }); afterEach(function() { assert.equal(this.x, 2); }); }); + + describe('timeout handling', function() { + describe('it does not reset the control flow on a non-timeout', function() { + var flowReset = false; + + beforeEach(function() { + flowReset = false; + promise.controlFlow().once( + promise.ControlFlow.EventType.RESET, onreset); + }); + + test.it('', function() { + this.timeout(100); + return promise.delayed(50); + }); + + afterEach(function() { + assert.ok(!flowReset); + promise.controlFlow().removeListener( + promise.ControlFlow.EventType.RESET, onreset); + }); + + function onreset() { + flowReset = true; + } + }); + + describe('it resets the control flow after a timeout' ,function() { + var timeoutErr, flowReset; + + beforeEach(function() { + flowReset = false; + promise.controlFlow().once( + promise.ControlFlow.EventType.RESET, onreset); + }); + + test.it('', function() { + var callback = this.runnable().callback; + var test = this; + this.runnable().callback = function(err) { + timeoutErr = err; + // Reset our timeout to 0 so Mocha does not fail the test. + test.timeout(0); + // When we invoke the real callback, do not pass along the error so + // Mocha does not fail the test. + return callback.call(this); + }; + + test.timeout(50); + return promise.defer().promise; + }); + + afterEach(function() { + promise.controlFlow().removeListener( + promise.ControlFlow.EventType.RESET, onreset); + assert.ok(flowReset, 'control flow was not reset after a timeout'); + }); + + function onreset() { + flowReset = true; + } + }); + }); +}); + +describe('Mocha async "done" support', function() { + this.timeout(2*1000); + + var waited = false; + var DELAY = 100; // ms enough to notice + + // Each test asynchronously sets waited to true, so clear/check waited + // before/after: + beforeEach(function() { + waited = false; + }); + + afterEach(function() { + assert.strictEqual(waited, true); + }); + + // --- First, vanilla mocha "it" should support the "done" callback correctly. + + // This 'it' should block until 'done' is invoked + it('vanilla delayed', function(done) { + setTimeout(function delayedVanillaTimeout() { + waited = true; + done(); + }, DELAY); + }); + + // --- Now with the webdriver wrappers for 'it' should support the "done" callback: + + test.it('delayed', function(done) { + assert(done); + assert.strictEqual(typeof done, 'function'); + //console.log(done.name); + //console.log(done.toString()); + setTimeout(function delayedTimeoutCallback() { + waited = true; + done(); + }, DELAY); + }); + + // --- And test that the webdriver wrapper for 'it' works with a returned promise, too: + + test.it('delayed by promise', function() { + var defer = promise.defer(); + setTimeout(function delayedPromiseCallback() { + waited = true; + defer.fulfill('ignored'); + }); + return defer.promise; + }); + +}); + +describe('ControlFlow and "done" work together', function() { + var flow, order; + before(function() { + order = []; + flow = promise.controlFlow(); + flow.execute(function() { order.push(1); }); + }); + + test.it('control flow updates and async done', function(done) { + flow.execute(function() { order.push(2); }); + flow.execute(function() { order.push(3); }).then(function() { + order.push(4); + }); + done(); + }) + + after(function() { + assert.deepEqual([1, 2, 3, 4], order); + }) }); diff --git a/test/upload_test.js b/test/upload_test.js new file mode 100644 index 0000000..f7fd907 --- /dev/null +++ b/test/upload_test.js @@ -0,0 +1,85 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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'); + +var Browser = require('..').Browser, + By = require('..').By, + until = require('..').until, + io = require('../io'), + remote = require('../remote'), + assert = require('../testing/assert'), + test = require('../lib/test'), + Pages = test.Pages; + +test.suite(function(env) { + var LOREM_IPSUM_TEXT = 'lorem ipsum dolor sit amet'; + var FILE_HTML = '
        ' + LOREM_IPSUM_TEXT + '
        '; + + var fp; + test.before(function() { + return fp = io.tmpFile().then(function(fp) { + fs.writeFileSync(fp, FILE_HTML); + return fp; + }); + }) + + var driver; + test.before(function() { + driver = env.builder().build(); + }); + + test.after(function() { + if (driver) { + driver.quit(); + } + }); + + test.ignore(env.browsers( + Browser.IPAD, + Browser.IPHONE, + // Uploads broken in PhantomJS 2.0. + // See https://github.com/ariya/phantomjs/issues/12506 + Browser.PHANTOM_JS, + Browser.SAFARI)). + it('can upload files', function() { + driver.setFileDetector(new remote.FileDetector); + + driver.get(Pages.uploadPage); + + var fp = driver.call(function() { + return io.tmpFile().then(function(fp) { + fs.writeFileSync(fp, FILE_HTML); + return fp; + }); + }); + + driver.findElement(By.id('upload')).sendKeys(fp); + driver.findElement(By.id('go')).submit(); + + // Uploading files across a network may take a while, even if they're small. + var label = driver.findElement(By.id('upload_label')); + driver.wait(until.elementIsNotVisible(label), + 10 * 1000, 'File took longer than 10 seconds to upload!'); + + driver.switchTo().frame('upload_target'); + assert(driver.findElement(By.css('body')).getText()) + .equalTo(LOREM_IPSUM_TEXT); + }); +}); diff --git a/test/window_test.js b/test/window_test.js index 2abd10e..959c9dc 100644 --- a/test/window_test.js +++ b/test/window_test.js @@ -1,29 +1,34 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 assert = require('../testing/assert'), - test = require('../lib/test'), - Browser = test.Browser; +var Browser = require('..').Browser, + assert = require('../testing/assert'), + test = require('../lib/test'); test.suite(function(env) { var driver; - beforeEach(function() { - driver = env.driver; + + test.before(function() { driver = env.builder().build(); }); + test.after(function() { driver.quit(); }); + + test.beforeEach(function() { driver.switchTo().defaultContent(); }); @@ -49,13 +54,20 @@ test.suite(function(env) { driver.manage().window().setPosition(position.x + 10, position.y + 10); // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0) - if (env.browser === Browser.PHANTOMJS) { + if (env.currentBrowser() === Browser.PHANTOM_JS) { driver.manage().window().getPosition().then(function(position) { assert(position.x).equalTo(0); assert(position.y).equalTo(0); }); } else { - driver.wait(forPositionToBe(position.x + 10, position.y + 10), 1000); + var dx = position.x + 10; + var dy = position.y + 10; + // On OSX, Safari position's the window relative to below the menubar + // at the top of the screen, which is 23 pixels tall. + if (env.currentBrowser() === Browser.SAFARI && + process.platform === 'darwin') { + dy += 23; + } } }); }); @@ -68,13 +80,21 @@ test.suite(function(env) { driver.manage().window().setPosition(position.x + 10, position.y + 10); // For phantomjs, setPosition is a no-op and the "window" stays at (0, 0) - if (env.browser === Browser.PHANTOMJS) { + if (env.currentBrowser() === Browser.PHANTOM_JS) { driver.manage().window().getPosition().then(function(position) { assert(position.x).equalTo(0); assert(position.y).equalTo(0); }); } else { - driver.wait(forPositionToBe(position.x + 10, position.y + 10), 1000); + var dx = position.x + 10; + var dy = position.y + 10; + // On OSX, Safari position's the window relative to below the menubar + // at the top of the screen, which is 23 pixels tall. + if (env.currentBrowser() === Browser.SAFARI && + process.platform === 'darwin') { + dy += 23; + } + driver.wait(forPositionToBe(dx, dy), 1000); } }); }); @@ -83,7 +103,7 @@ test.suite(function(env) { driver.manage().window().getSize().then(function(size) { driver.manage().window().setSize(size.width + dx, size.height + dy); driver.wait(forSizeToBe(size.width + dx, size.height + dy), 1000); - }) + }); } function forSizeToBe(w, h) { @@ -100,7 +120,8 @@ test.suite(function(env) { return position.x === x && // On OSX, the window height may be bumped down 22px for the top // status bar. - (position.y >= y && position.y <= (y + 22)); + // On Linux, Opera's window position will be off by 28px. + (position.y >= y && position.y <= (y + 28)); }); }; } diff --git a/testing/assert.js b/testing/assert.js index 80fcda3..a4d729b 100644 --- a/testing/assert.js +++ b/testing/assert.js @@ -1,35 +1,35 @@ -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Defines a library that simplifies writing assertions against * promised values. * - *
        - *
        - * NOTE: This module is considered experimental and is subject to - * change, or removal, at any time! - *
        - *
        + * >
        + * > __NOTE:__ This module is considered experimental and is subject to + * > change, or removal, at any time! + * >
        * * Sample usage: - *
        
        - * var driver = new webdriver.Builder().build();
        - * driver.get('http://www.google.com');
          *
        - * assert(driver.getTitle()).equalTo('Google');
        - * 
        + * var driver = new webdriver.Builder().build(); + * driver.get('http://www.google.com'); + * + * assert(driver.getTitle()).equalTo('Google'); */ var base = require('../_base'), @@ -39,5 +39,23 @@ var base = require('../_base'), // PUBLIC API -/** @type {webdriver.testing.assert.} */ -module.exports = assert; +/** + * Creates a new assertion. + * @param {*} value The value to perform an assertion on. + * @return {!webdriver.testing.Assertion} The new assertion. + */ +module.exports = function(value) { + return assert(value); +}; + + +/** + * Registers a new assertion to expose from the + * {@link webdriver.testing.Assertion} prototype. + * @param {string} name The assertion name. + * @param {(function(new: goog.labs.testing.Matcher, *)| + * {matches: function(*): boolean, + * describe: function(): string})} matcherTemplate Either the + * matcher constructor to use, or an object literal defining a matcher. + */ +module.exports.register = assert.register; diff --git a/testing/index.js b/testing/index.js index 12ac341..2a1c4cf 100644 --- a/testing/index.js +++ b/testing/index.js @@ -1,85 +1,73 @@ -// Copyright 2013 Selenium committers -// Copyright 2013 Software Freedom Conservancy +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you 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 // -// 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 // -// 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. +// 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 Provides wrappers around the following global functions from - * Mocha's BDD interface: - *
          - *
        • after - *
        • afterEach - *
        • before - *
        • beforeEach - *
        • it - *
        • it.only - *
        • it.skip - *
        • xit - *
        + * [Mocha's BDD interface](https://github.com/mochajs/mocha): * - *

        The provided wrappers leverage the webdriver.promise.ControlFlow to - * simplify writing asynchronous tests: - *

        
        - * var webdriver = require('selenium-webdriver'),
        - *     remote = require('selenium-webdriver/remote'),
        - *     test = require('selenium-webdriver/testing');
        + * - after
        + * - afterEach
        + * - before
        + * - beforeEach
        + * - it
        + * - it.only
        + * - it.skip
        + * - xit
          *
        - * test.describe('Google Search', function() {
        - *   var driver, server;
        + * The provided wrappers leverage the {@link webdriver.promise.ControlFlow}
        + * to simplify writing asynchronous tests:
          *
        - *   test.before(function() {
        - *     server = new remote.SeleniumServer({
        - *       jar: 'path/to/selenium-server-standalone.jar'
        - *     });
        - *     server.start();
        + *     var By = require('selenium-webdriver').By,
        + *         until = require('selenium-webdriver').until,
        + *         firefox = require('selenium-webdriver/firefox'),
        + *         test = require('selenium-webdriver/testing');
          *
        - *     driver = new webdriver.Builder().
        - *         withCapabilities({'browserName': 'firefox'}).
        - *         usingServer(server.address()).
        - *         build();
        - *   });
        + *     test.describe('Google Search', function() {
        + *       var driver;
          *
        - *   test.after(function() {
        - *     driver.quit();
        - *     server.stop();
        - *   });
        + *       test.before(function() {
        + *         driver = new firefox.Driver();
        + *       });
          *
        - *   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;
        + *       test.after(function() {
        + *         driver.quit();
          *       });
        - *     }, 1000, 'Waiting for title to update');
        - *   });
        - * });
        - * 
        * - *

        You may conditionally suppress a test function using the exported + * test.it('should append query to title', function() { + * driver.get('http://www.google.com/ncr'); + * driver.findElement(By.name('q')).sendKeys('webdriver'); + * driver.findElement(By.name('btnG')).click(); + * driver.wait(until.titleIs('webdriver - Google Search'), 1000); + * }); + * }); + * + * 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: - *

        
        - *   test.ignore(maybe()).it('is flaky', function() {
        - *     if (Math.random() < 0.5) throw Error();
        - *   });
          *
        - *   function maybe() { return Math.random() < 0.5; }
        - * 
        + * test.ignore(maybe()).it('is flaky', function() { + * if (Math.random() < 0.5) throw Error(); + * }); + * + * function maybe() { return Math.random() < 0.5; } */ -var flow = require('..').promise.controlFlow(); +var promise = require('..').promise; +var flow = promise.controlFlow(); /** @@ -103,32 +91,67 @@ function seal(fn) { */ function wrapped(globalFn) { return function() { - switch (arguments.length) { - case 1: - globalFn(asyncTestFn(arguments[0])); - break; - - case 2: - globalFn(arguments[0], asyncTestFn(arguments[1])); - break; - - default: - throw Error('Invalid # arguments: ' + arguments.length); + if (arguments.length === 1) { + return globalFn(makeAsyncTestFn(arguments[0])); + } + else if (arguments.length === 2) { + return globalFn(arguments[0], makeAsyncTestFn(arguments[1])); + } + else { + throw Error('Invalid # arguments: ' + arguments.length); } }; +} - function asyncTestFn(fn) { - return function(done) { - this.timeout(0); - var timeout = this.timeout; - this.timeout = undefined; // Do not let tests change the timeout. - try { - flow.execute(fn.bind(this)).then(seal(done), done); - } finally { - this.timeout = timeout; - } +/** + * Make a wrapper to invoke caller's test function, fn. Run the test function + * within a ControlFlow. + * + * Should preserve the semantics of Mocha's Runnable.prototype.run (See + * https://github.com/mochajs/mocha/blob/master/lib/runnable.js#L192) + * + * @param {Function} fn + * @return {Function} + */ +function makeAsyncTestFn(fn) { + var async = fn.length > 0; // if test function expects a callback, its "async" + + var ret = function(done) { + var runnable = this.runnable(); + var mochaCallback = runnable.callback; + runnable.callback = function() { + flow.reset(); + return mochaCallback.apply(this, arguments); }; - } + + var testFn = fn.bind(this); + flow.execute(function controlFlowExecute() { + return new promise.Promise(function(fulfill, reject) { + if (async) { + // If testFn is async (it expects a done callback), resolve the promise of this + // test whenever that callback says to. Any promises returned from testFn are + // ignored. + testFn(function testFnDoneCallback(err) { + if (err) { + reject(err); + } else { + fulfill(); + } + }); + } else { + // Without a callback, testFn can return a promise, or it will + // be assumed to have completed synchronously + fulfill(testFn()); + } + }, flow); + }, runnable.fullTitle()).then(seal(done), done); + }; + + ret.toString = function() { + return fn.toString(); + }; + + return ret; }